diff options
Diffstat (limited to 'java/com/android/modules')
-rw-r--r-- | java/com/android/modules/expresslog/Android.bp | 39 | ||||
-rw-r--r-- | java/com/android/modules/expresslog/Counter.java | 67 | ||||
-rw-r--r-- | java/com/android/modules/expresslog/Histogram.java | 227 | ||||
-rw-r--r-- | java/com/android/modules/expresslog/OWNERS | 10 | ||||
-rw-r--r-- | java/com/android/modules/expresslog/TEST_MAPPING | 12 | ||||
-rw-r--r-- | java/com/android/modules/expresslog/Utils.java | 21 |
6 files changed, 376 insertions, 0 deletions
diff --git a/java/com/android/modules/expresslog/Android.bp b/java/com/android/modules/expresslog/Android.bp new file mode 100644 index 0000000..cacc7f8 --- /dev/null +++ b/java/com/android/modules/expresslog/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2023 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library { + name: "modules-utils-expresslog", + defaults: ["modules-utils-defaults"], + min_sdk_version: "30", + srcs: [ + "*.java", + ":statslog-expresslog-java-gen", + ], + libs: [ + "framework-statsd", + ], +} + +genrule { + name: "statslog-expresslog-java-gen", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --java $(out) --module expresslog" + + " --javaPackage com.android.modules.expresslog --javaClass StatsExpressLog", + out: ["com/android/modules/expresslog/StatsExpressLog.java"], +} diff --git a/java/com/android/modules/expresslog/Counter.java b/java/com/android/modules/expresslog/Counter.java new file mode 100644 index 0000000..b788c3f --- /dev/null +++ b/java/com/android/modules/expresslog/Counter.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 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.modules.expresslog; + +import android.annotation.NonNull; + +import com.android.modules.expresslog.StatsExpressLog; + +/** Counter encapsulates StatsD write API calls */ +public final class Counter { + + // Not instantiable. + private Counter() {} + + /** + * Increments Telemetry Express Counter metric by 1 + * @param metricId to log, no-op if metricId is not defined in the TeX catalog + */ + public static void logIncrement(@NonNull String metricId) { + logIncrement(metricId, 1); + } + + /** + * Increments Telemetry Express Counter metric by 1 + * @param metricId to log, no-op if metricId is not defined in the TeX catalog + * @param uid used as a dimension for the count metric + */ + public static void logIncrementWithUid(@NonNull String metricId, int uid) { + logIncrementWithUid(metricId, uid, 1); + } + + /** + * Increments Telemetry Express Counter metric by arbitrary value + * @param metricId to log, no-op if metricId is not defined in the TeX catalog + * @param amount to increment counter + */ + public static void logIncrement(@NonNull String metricId, long amount) { + final long metricIdHash = Utils.hashString(metricId); + StatsExpressLog.write(StatsExpressLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount); + } + + /** + * Increments Telemetry Express Counter metric by arbitrary value + * @param metricId to log, no-op if metricId is not defined in the TeX catalog + * @param uid used as a dimension for the count metric + * @param amount to increment counter + */ + public static void logIncrementWithUid(@NonNull String metricId, int uid, long amount) { + final long metricIdHash = Utils.hashString(metricId); + StatsExpressLog.write( + StatsExpressLog.EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid); + } +} diff --git a/java/com/android/modules/expresslog/Histogram.java b/java/com/android/modules/expresslog/Histogram.java new file mode 100644 index 0000000..be300bf --- /dev/null +++ b/java/com/android/modules/expresslog/Histogram.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2023 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.modules.expresslog; + +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; + +import com.android.modules.expresslog.StatsExpressLog; + +import java.util.Arrays; + +/** Histogram encapsulates StatsD write API calls */ +public final class Histogram { + + private final long mMetricIdHash; + private final BinOptions mBinOptions; + + /** + * Creates Histogram metric logging wrapper + * + * @param metricId to log, logging will be no-op if metricId is not defined in the TeX catalog + * @param binOptions to calculate bin index for samples + */ + public Histogram(@NonNull String metricId, @NonNull BinOptions binOptions) { + mMetricIdHash = Utils.hashString(metricId); + mBinOptions = binOptions; + } + + /** + * Logs increment sample count for automatically calculated bin + * + * @param sample value + */ + public void logSample(float sample) { + final int binIndex = mBinOptions.getBinForSample(sample); + StatsExpressLog.write(StatsExpressLog.EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, + /*count*/ 1, binIndex); + } + + /** + * Logs increment sample count for automatically calculated bin + * + * @param uid used as a dimension for the count metric + * @param sample value + */ + public void logSampleWithUid(int uid, float sample) { + final int binIndex = mBinOptions.getBinForSample(sample); + StatsExpressLog.write(StatsExpressLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED, + mMetricIdHash, /*count*/ 1, binIndex, uid); + } + + /** Used by Histogram to map data sample to corresponding bin */ + public interface BinOptions { + /** + * Returns bins count to be used by a histogram + * + * @return bins count used to initialize Options, including overflow & underflow bins + */ + int getBinsCount(); + + /** + * Returns bin index for the input sample value + * index == 0 stands for underflow + * index == getBinsCount() - 1 stands for overflow + * + * @return zero based index + */ + int getBinForSample(float sample); + } + + /** Used by Histogram to map data sample to corresponding bin for uniform bins */ + public static final class UniformOptions implements BinOptions { + + private final int mBinCount; + private final float mMinValue; + private final float mExclusiveMaxValue; + private final float mBinSize; + + /** + * Creates options for uniform (linear) sized bins + * + * @param binCount amount of histogram bins. 2 bin indexes will be calculated + * automatically to represent underflow & overflow bins + * @param minValue is included in the first bin, values less than minValue + * go to underflow bin + * @param exclusiveMaxValue is included in the overflow bucket. For accurate + * measure up to kMax, then exclusiveMaxValue + * should be set to kMax + 1 + */ + public UniformOptions(@IntRange(from = 1) int binCount, float minValue, + float exclusiveMaxValue) { + if (binCount < 1) { + throw new IllegalArgumentException("Bin count should be positive number"); + } + + if (exclusiveMaxValue <= minValue) { + throw new IllegalArgumentException("Bins range invalid (maxValue < minValue)"); + } + + mMinValue = minValue; + mExclusiveMaxValue = exclusiveMaxValue; + mBinSize = (mExclusiveMaxValue - minValue) / binCount; + + // Implicitly add 2 for the extra underflow & overflow bins + mBinCount = binCount + 2; + } + + @Override + public int getBinsCount() { + return mBinCount; + } + + @Override + public int getBinForSample(float sample) { + if (sample < mMinValue) { + // goes to underflow + return 0; + } else if (sample >= mExclusiveMaxValue) { + // goes to overflow + return mBinCount - 1; + } + return (int) ((sample - mMinValue) / mBinSize + 1); + } + } + + /** Used by Histogram to map data sample to corresponding bin for scaled bins */ + public static final class ScaledRangeOptions implements BinOptions { + // store minimum value per bin + final long[] mBins; + + /** + * Creates options for scaled range bins + * + * @param binCount amount of histogram bins. 2 bin indexes will be calculated + * automatically to represent underflow & overflow bins + * @param minValue is included in the first bin, values less than minValue + * go to underflow bin + * @param firstBinWidth used to represent first bin width and as a reference to calculate + * width for consecutive bins + * @param scaleFactor used to calculate width for consecutive bins + */ + public ScaledRangeOptions(@IntRange(from = 1) int binCount, int minValue, + @FloatRange(from = 1.f) float firstBinWidth, + @FloatRange(from = 1.f) float scaleFactor) { + if (binCount < 1) { + throw new IllegalArgumentException("Bin count should be positive number"); + } + + if (firstBinWidth < 1.f) { + throw new IllegalArgumentException( + "First bin width invalid (should be 1.f at minimum)"); + } + + if (scaleFactor < 1.f) { + throw new IllegalArgumentException( + "Scaled factor invalid (should be 1.f at minimum)"); + } + + // precalculating bins ranges (no need to create a bin for underflow reference value) + mBins = initBins(binCount + 1, minValue, firstBinWidth, scaleFactor); + } + + @Override + public int getBinsCount() { + return mBins.length + 1; + } + + @Override + public int getBinForSample(float sample) { + if (sample < mBins[0]) { + // goes to underflow + return 0; + } else if (sample >= mBins[mBins.length - 1]) { + // goes to overflow + return mBins.length; + } + + return lower_bound(mBins, (long) sample) + 1; + } + + // To find lower bound using binary search implementation of Arrays utility class + private static int lower_bound(long[] array, long sample) { + int index = Arrays.binarySearch(array, sample); + // If key is not present in the array + if (index < 0) { + // Index specify the position of the key when inserted in the sorted array + // so the element currently present at this position will be the lower bound + return Math.abs(index) - 2; + } + return index; + } + + private static long[] initBins(int count, int minValue, float firstBinWidth, + float scaleFactor) { + long[] bins = new long[count]; + bins[0] = minValue; + double lastWidth = firstBinWidth; + for (int i = 1; i < count; i++) { + // current bin minValue = previous bin width * scaleFactor + double currentBinMinValue = bins[i - 1] + lastWidth; + if (currentBinMinValue > Integer.MAX_VALUE) { + throw new IllegalArgumentException( + "Attempted to create a bucket larger than maxint"); + } + + bins[i] = (long) currentBinMinValue; + lastWidth *= scaleFactor; + } + return bins; + } + } +} diff --git a/java/com/android/modules/expresslog/OWNERS b/java/com/android/modules/expresslog/OWNERS new file mode 100644 index 0000000..d3a5812 --- /dev/null +++ b/java/com/android/modules/expresslog/OWNERS @@ -0,0 +1,10 @@ +# Bug component: 719316 +# Stats/expresslog + +jeffreyhuang@google.com +muhammadq@google.com +rslawik@google.com +sharaienko@google.com +singhtejinder@google.com +tsaichristine@google.com +yaochen@google.com diff --git a/java/com/android/modules/expresslog/TEST_MAPPING b/java/com/android/modules/expresslog/TEST_MAPPING new file mode 100644 index 0000000..e658d7a --- /dev/null +++ b/java/com/android/modules/expresslog/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "ExpressLogApisTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/java/com/android/modules/expresslog/Utils.java b/java/com/android/modules/expresslog/Utils.java new file mode 100644 index 0000000..fde90fc --- /dev/null +++ b/java/com/android/modules/expresslog/Utils.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.modules.expresslog; + +final class Utils { + static native long hashString(String stringToHash); +} |