| /* |
| * Copyright (C) 2019 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.internal.os.statsd.libstats; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import android.app.StatsManager; |
| import android.content.Context; |
| import android.util.Log; |
| import android.util.StatsLog; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.filters.MediumTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.internal.os.StatsdConfigProto.AtomMatcher; |
| import com.android.internal.os.StatsdConfigProto.FieldFilter; |
| import com.android.internal.os.StatsdConfigProto.GaugeMetric; |
| import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; |
| import com.android.internal.os.StatsdConfigProto.StatsdConfig; |
| import com.android.internal.os.StatsdConfigProto.TimeUnit; |
| import com.android.internal.os.statsd.StatsConfigUtils; |
| import com.android.internal.os.statsd.protos.TestAtoms; |
| import com.android.os.AtomsProto.Atom; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.List; |
| |
| /** |
| * Test puller registration. |
| */ |
| @MediumTest |
| @RunWith(AndroidJUnit4.class) |
| public class LibStatsPullTests { |
| private static final String LOG_TAG = LibStatsPullTests.class.getSimpleName(); |
| private static final int SHORT_SLEEP_MILLIS = 250; |
| private static final int LONG_SLEEP_MILLIS = 1_000; |
| private Context mContext; |
| private static final int PULL_ATOM_TAG = 150030; |
| private static final int APP_BREADCRUMB_LABEL = 3; |
| private static int sPullReturnValue; |
| private static long sConfigId; |
| private static long sPullLatencyMillis; |
| private static long sPullTimeoutNs; |
| private static long sCoolDownNs; |
| private static int sAtomsPerPull; |
| |
| static { |
| System.loadLibrary("statspull_testhelper"); |
| } |
| |
| /** |
| * Setup the tests. Initialize shared data. |
| */ |
| @Before |
| public void setup() { |
| // Debug.waitForDebugger(); |
| mContext = InstrumentationRegistry.getTargetContext(); |
| assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull(); |
| sPullReturnValue = StatsManager.PULL_SUCCESS; |
| sPullLatencyMillis = 0; |
| sPullTimeoutNs = 10_000_000_000L; |
| sCoolDownNs = 1_000_000_000L; |
| sAtomsPerPull = 1; |
| } |
| |
| /** |
| * Teardown the tests. |
| */ |
| @After |
| public void tearDown() throws Exception { |
| unregisterStatsPuller(PULL_ATOM_TAG); |
| StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| Context.STATS_MANAGER); |
| statsManager.removeConfig(sConfigId); |
| } |
| |
| /** |
| * Tests adding a puller callback and that pulls complete successfully. |
| */ |
| @Test |
| public void testPullAtomCallbackRegistration() throws Exception { |
| StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| Context.STATS_MANAGER); |
| // Upload a config that captures that pulled atom. |
| createAndAddConfigToStatsd(statsManager); |
| |
| // Add the puller. |
| registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue, |
| sPullLatencyMillis, sAtomsPerPull); |
| Thread.sleep(SHORT_SLEEP_MILLIS); |
| StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| // Let the current bucket finish. |
| Thread.sleep(LONG_SLEEP_MILLIS); |
| List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
| unregisterStatsPuller(PULL_ATOM_TAG); |
| assertThat(data.size()).isEqualTo(1); |
| TestAtoms.PullCallbackAtomWrapper atomWrapper = null; |
| try { |
| atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser() |
| .parseFrom(data.get(0).toByteArray()); |
| } catch (Exception e) { |
| Log.e(LOG_TAG, "Failed to parse primitive atoms"); |
| } |
| assertThat(atomWrapper).isNotNull(); |
| assertThat(atomWrapper.hasPullCallbackAtom()).isTrue(); |
| TestAtoms.PullCallbackAtom atom = |
| atomWrapper.getPullCallbackAtom(); |
| assertThat(atom.getLongVal()).isEqualTo(1); |
| } |
| |
| /** |
| * Tests that a failed pull is skipped. |
| */ |
| @Test |
| public void testPullAtomCallbackFailure() throws Exception { |
| StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| Context.STATS_MANAGER); |
| createAndAddConfigToStatsd(statsManager); |
| sPullReturnValue = StatsManager.PULL_SKIP; |
| // Add the puller. |
| registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue, |
| sPullLatencyMillis, sAtomsPerPull); |
| Thread.sleep(SHORT_SLEEP_MILLIS); |
| StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| // Let the current bucket finish. |
| Thread.sleep(LONG_SLEEP_MILLIS); |
| List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
| unregisterStatsPuller(PULL_ATOM_TAG); |
| assertThat(data.size()).isEqualTo(0); |
| } |
| |
| /** |
| * Tests that a pull that times out is skipped. |
| */ |
| @Test |
| public void testPullAtomCallbackTimeout() throws Exception { |
| StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| Context.STATS_MANAGER); |
| createAndAddConfigToStatsd(statsManager); |
| // The puller will sleep for 1.5 sec. |
| sPullLatencyMillis = 1_500; |
| // 1 second timeout |
| sPullTimeoutNs = 1_000_000_000; |
| |
| // Add the puller. |
| registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue, |
| sPullLatencyMillis, sAtomsPerPull); |
| Thread.sleep(SHORT_SLEEP_MILLIS); |
| StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| // Let the current bucket finish and the pull timeout. |
| Thread.sleep(sPullLatencyMillis * 2); |
| List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
| unregisterStatsPuller(PULL_ATOM_TAG); |
| assertThat(data.size()).isEqualTo(0); |
| } |
| |
| /** |
| * Tests that 2 pulls in quick succession use the cache instead of pulling again. |
| */ |
| @Test |
| public void testPullAtomCallbackCache() throws Exception { |
| StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| Context.STATS_MANAGER); |
| createAndAddConfigToStatsd(statsManager); |
| |
| // Set the cooldown to 10 seconds |
| sCoolDownNs = 10_000_000_000L; |
| // Add the puller. |
| registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue, |
| sPullLatencyMillis, sAtomsPerPull); |
| |
| Thread.sleep(SHORT_SLEEP_MILLIS); |
| StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| // Pull from cache. |
| StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| Thread.sleep(LONG_SLEEP_MILLIS); |
| List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
| unregisterStatsPuller(PULL_ATOM_TAG); |
| assertThat(data.size()).isEqualTo(2); |
| for (int i = 0; i < data.size(); i++) { |
| TestAtoms.PullCallbackAtomWrapper atomWrapper = null; |
| try { |
| atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser() |
| .parseFrom(data.get(i).toByteArray()); |
| } catch (Exception e) { |
| Log.e(LOG_TAG, "Failed to parse primitive atoms"); |
| } |
| assertThat(atomWrapper).isNotNull(); |
| assertThat(atomWrapper.hasPullCallbackAtom()).isTrue(); |
| TestAtoms.PullCallbackAtom atom = |
| atomWrapper.getPullCallbackAtom(); |
| assertThat(atom.getLongVal()).isEqualTo(1); |
| } |
| } |
| |
| /** |
| * Tests that a pull that returns 1000 stats events works properly. |
| */ |
| @Test |
| public void testPullAtomCallbackStress() throws Exception { |
| StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| Context.STATS_MANAGER); |
| // Upload a config that captures that pulled atom. |
| createAndAddConfigToStatsd(statsManager); |
| sAtomsPerPull = 1000; |
| // Add the puller. |
| registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue, |
| sPullLatencyMillis, sAtomsPerPull); |
| |
| Thread.sleep(SHORT_SLEEP_MILLIS); |
| StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| // Let the current bucket finish. |
| Thread.sleep(LONG_SLEEP_MILLIS); |
| List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
| statsManager.unregisterPullAtomCallback(PULL_ATOM_TAG); |
| assertThat(data.size()).isEqualTo(sAtomsPerPull); |
| |
| for (int i = 0; i < data.size(); i++) { |
| TestAtoms.PullCallbackAtomWrapper atomWrapper = null; |
| try { |
| atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser() |
| .parseFrom(data.get(i).toByteArray()); |
| } catch (Exception e) { |
| Log.e(LOG_TAG, "Failed to parse primitive atoms"); |
| } |
| assertThat(atomWrapper).isNotNull(); |
| assertThat(atomWrapper.hasPullCallbackAtom()).isTrue(); |
| TestAtoms.PullCallbackAtom atom = |
| atomWrapper.getPullCallbackAtom(); |
| assertThat(atom.getLongVal()).isEqualTo(1); |
| } |
| } |
| |
| private void createAndAddConfigToStatsd(StatsManager statsManager) throws Exception { |
| sConfigId = System.currentTimeMillis(); |
| long triggerMatcherId = sConfigId + 10; |
| long pullerMatcherId = sConfigId + 11; |
| long metricId = sConfigId + 100; |
| StatsdConfig config = StatsConfigUtils.getSimpleTestConfig(sConfigId) |
| .addAtomMatcher( |
| StatsConfigUtils.getAppBreadcrumbMatcher(triggerMatcherId, |
| APP_BREADCRUMB_LABEL)) |
| .addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(pullerMatcherId) |
| .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder() |
| .setAtomId(PULL_ATOM_TAG)) |
| ) |
| .addGaugeMetric(GaugeMetric.newBuilder() |
| .setId(metricId) |
| .setWhat(pullerMatcherId) |
| .setTriggerEvent(triggerMatcherId) |
| .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true)) |
| .setBucket(TimeUnit.CTS) |
| .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES) |
| .setMaxNumGaugeAtomsPerBucket(1000) |
| ) |
| .build(); |
| statsManager.addConfig(sConfigId, config.toByteArray()); |
| assertThat(StatsConfigUtils.verifyValidConfigExists(statsManager, sConfigId)).isTrue(); |
| } |
| |
| private native void registerStatsPuller(int atomTag, long timeoutNs, long coolDownNs, |
| int pullReturnVal, long latencyMillis, int atomPerPull); |
| |
| private native void unregisterStatsPuller(int atomTag); |
| } |
| |