diff --git a/Android.bp b/Android.bp old mode 100644 new mode 100755 index d5e04f9f4e2d84b0eccb7307b0c68e2689f170a4..7f3243f13d197db3242b5057a2039bdd98fb6281 --- a/Android.bp +++ b/Android.bp @@ -249,8 +249,7 @@ java_library { "core/java/android/os/storage/IStorageEventListener.aidl", "core/java/android/os/storage/IStorageShutdownObserver.aidl", "core/java/android/os/storage/IObbActionListener.aidl", - "core/java/android/security/IConfirmationPromptCallback.aidl", - "core/java/android/security/IKeystoreService.aidl", + ":keystore_aidl", "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl", "core/java/android/service/autofill/IAutoFillService.aidl", "core/java/android/service/autofill/IAutofillFieldClassificationService.aidl", @@ -357,6 +356,7 @@ java_library { "core/java/android/speech/IRecognitionService.aidl", "core/java/android/speech/tts/ITextToSpeechCallback.aidl", "core/java/android/speech/tts/ITextToSpeechService.aidl", + "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl", "core/java/com/android/internal/app/IAppOpsCallback.aidl", "core/java/com/android/internal/app/IAppOpsService.aidl", "core/java/com/android/internal/app/IBatteryStats.aidl", @@ -373,7 +373,6 @@ java_library { "core/java/com/android/internal/appwidget/IAppWidgetHost.aidl", "core/java/com/android/internal/backup/IBackupTransport.aidl", "core/java/com/android/internal/backup/IObbBackupService.aidl", - "core/java/com/android/internal/car/ICarServiceHelper.aidl", "core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl", "core/java/com/android/internal/net/INetworkWatchlistManager.aidl", "core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl", @@ -384,6 +383,7 @@ java_library { "core/java/com/android/internal/policy/IShortcutService.aidl", "core/java/com/android/internal/os/IDropBoxManagerService.aidl", "core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl", + "core/java/com/android/internal/os/IRegionalizationService.aidl", "core/java/com/android/internal/os/IResultReceiver.aidl", "core/java/com/android/internal/os/IShellCallback.aidl", "core/java/com/android/internal/statusbar/IStatusBar.aidl", @@ -426,6 +426,7 @@ java_library { "media/java/android/media/IAudioFocusDispatcher.aidl", "media/java/android/media/IAudioRoutesObserver.aidl", "media/java/android/media/IAudioService.aidl", + "media/java/android/media/IAudioServerStateDispatcher.aidl", "media/java/android/media/IMediaHTTPConnection.aidl", "media/java/android/media/IMediaHTTPService.aidl", "media/java/android/media/IMediaResourceMonitor.aidl", @@ -579,7 +580,6 @@ java_library { "wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl", "wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl", "wifi/java/android/net/wifi/IWifiScanner.aidl", - "wifi/java/android/net/wifi/IRttManager.aidl", "packages/services/PacProcessor/com/android/net/IProxyService.aidl", "packages/services/Proxy/com/android/net/IProxyCallback.aidl", "packages/services/Proxy/com/android/net/IProxyPortListener.aidl", @@ -644,6 +644,7 @@ java_library { "system/netd/server/binder", "system/vold/binder", "system/bt/binder", + "system/security/keystore/binder", ], }, @@ -672,8 +673,9 @@ java_library { "android.hardware.tv.input-V1.0-java-constants", "android.hardware.usb-V1.0-java-constants", "android.hardware.usb-V1.1-java-constants", - "android.hardware.vibrator-V1.0-java-constants", - "android.hardware.vibrator-V1.1-java-constants", + "android.hardware.vibrator-V1.0-java", + "android.hardware.vibrator-V1.1-java", + "android.hardware.vibrator-V1.2-java", "android.hardware.wifi-V1.0-java-constants", "android.hardware.radio-V1.0-java", "android.hardware.usb.gadget-V1.0-java", diff --git a/Android.mk b/Android.mk old mode 100644 new mode 100755 index a78a01a1600f55e4e94927dbf9815594b73d8ea7..58e21ffd161770b40bd6a14fac2c89e79d76042c --- a/Android.mk +++ b/Android.mk @@ -36,7 +36,7 @@ aidl_parcelables := define stubs-to-aidl-parcelables gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl aidl_parcelables += $$(gen) - $$(gen): $(call java-lib-header-files,$1) | $(HOST_OUT_EXECUTABLES)/sdkparcelables + $$(gen): $(call java-lib-header-files,$1) $(HOST_OUT_EXECUTABLES)/sdkparcelables @echo Extract SDK parcelables: $$@ rm -f $$@ $(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@ @@ -793,6 +793,8 @@ LOCAL_SRC_FILES := \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto) \ $(call all-proto-files-under, cmds/statsd/src) +# b/72714520 +LOCAL_ERROR_PRONE_FLAGS := -Xep:MissingOverride:OFF include $(BUILD_HOST_JAVA_LIBRARY) # ==== java proto device library (for test only) ============================== @@ -804,7 +806,7 @@ LOCAL_PROTOC_FLAGS := \ -Iexternal/protobuf/src LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ store_unknown_fields = true -LOCAL_JAVA_LIBRARIES := core-oj core-libart +LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto/android/os) diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 711c12d20a849d39029a8e3e9beafb4f451a76f5..2a67b7557f2cdf3d1f39c266d3c35a82c0c0deb8 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,6 +1,7 @@ [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} -fw core/java/android/ + graphics/java/android core/tests/coretests/src/android/ packages/PrintRecommendationService/ packages/PrintSpooler/ diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk index 75cb229e36d6fd8e8e7dbe6f9fd33e5a27e30984..b7b87dda1957f4534e50f7cd71f9343a131c6360 100644 --- a/apct-tests/perftests/core/Android.mk +++ b/apct-tests/perftests/core/Android.mk @@ -22,6 +22,8 @@ LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni # Use google-fonts/dancing-script for the performance metrics LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script +LOCAL_COMPATIBILITY_SUITE += device-tests + include $(BUILD_PACKAGE) include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f8fd51d7b0b676f85d2e83d79666d682b118660a --- /dev/null +++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018 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 android.app; + +import android.content.Context; +import android.content.Intent; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.perftests.utils.StubActivity; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +// Due to b/71353150, you might get "java.lang.AssertionError: Binder ProxyMap has too many +// entries", but it's flaky. Adding "Runtime.getRuntime().gc()" between each iteration solves +// the problem, but it doesn't seem like it's currently needed. + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class PendingIntentPerfTest { + + private Context mContext; + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private Intent mIntent; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + mIntent = StubActivity.createLaunchIntent(mContext); + } + + /** + * Benchmark time to create a PendingIntent. + */ + @Test + public void create() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + state.resumeTiming(); + + final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent, + 0); + + state.pauseTiming(); + pendingIntent.cancel(); + state.resumeTiming(); + } + } + + /** + * Benchmark time to create a PendingIntent with FLAG_CANCEL_CURRENT, already having an active + * PendingIntent. + */ + @Test + public void createWithCancelFlag() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0, + mIntent, 0); + state.resumeTiming(); + + final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent, + PendingIntent.FLAG_CANCEL_CURRENT); + + state.pauseTiming(); + pendingIntent.cancel(); + state.resumeTiming(); + } + } + + /** + * Benchmark time to create a PendingIntent with FLAG_UPDATE_CURRENT, already having an active + * PendingIntent. + */ + @Test + public void createWithUpdateFlag() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0, + mIntent, 0); + state.resumeTiming(); + + final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + state.pauseTiming(); + previousPendingIntent.cancel(); + pendingIntent.cancel(); + state.resumeTiming(); + } + } + + /** + * Benchmark time to cancel a PendingIntent. + */ + @Test + public void cancel() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, + mIntent, 0); + state.resumeTiming(); + + pendingIntent.cancel(); + } + } +} + diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java similarity index 73% rename from apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java rename to apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java index fc6302ea93942d30532e9d5ae185ead48eb31d78..73e17242ae785749a839fbf1124c072ca3d45284 100644 --- a/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java +++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java @@ -45,7 +45,7 @@ import java.util.Random; @LargeTest @RunWith(AndroidJUnit4.class) -public class MeasuredTextMemoryUsageTest { +public class PrecomputedTextMemoryUsageTest { private static final int WORD_LENGTH = 9; // Random word has 9 characters. private static final boolean NO_STYLE_TEXT = false; @@ -53,7 +53,7 @@ public class MeasuredTextMemoryUsageTest { private static int TRIAL_COUNT = 100; - public MeasuredTextMemoryUsageTest() {} + public PrecomputedTextMemoryUsageTest() {} private TextPerfUtils mTextUtil = new TextPerfUtils(); @@ -77,13 +77,16 @@ public class MeasuredTextMemoryUsageTest { @Test public void testMemoryUsage_NoHyphenation() { int[] memories = new int[TRIAL_COUNT]; - // Report median of randomly generated MeasuredText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - memories[i] = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build().getMemoryUsage(); + .build(); + + // Report median of randomly generated PrecomputedText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + memories[i] = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param) + .getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation"); } @@ -91,13 +94,16 @@ public class MeasuredTextMemoryUsageTest { @Test public void testMemoryUsage_Hyphenation() { int[] memories = new int[TRIAL_COUNT]; - // Report median of randomly generated MeasuredText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - memories[i] = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build().getMemoryUsage(); + .build(); + + // Report median of randomly generated PrecomputedText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + memories[i] = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param) + .getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation"); } @@ -105,13 +111,16 @@ public class MeasuredTextMemoryUsageTest { @Test public void testMemoryUsage_NoHyphenation_WidthOnly() { int[] memories = new int[TRIAL_COUNT]; - // Report median of randomly generated MeasuredText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - memories[i] = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(false /* width only */).getMemoryUsage(); + .build(); + + // Report median of randomly generated PrecomputedText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length()) + .getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly"); } @@ -119,13 +128,16 @@ public class MeasuredTextMemoryUsageTest { @Test public void testMemoryUsage_Hyphenatation_WidthOnly() { int[] memories = new int[TRIAL_COUNT]; - // Report median of randomly generated MeasuredText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - memories[i] = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(false /* width only */).getMemoryUsage(); + .build(); + + // Report median of randomly generated PrecomputedText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length()) + .getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly"); } diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java similarity index 66% rename from apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java rename to apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java index 98f2bd5e573652d1feba4220546293bef82d1865..1cd0ae13069bb42f3aefbd2b210ae41546edb25c 100644 --- a/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java @@ -42,7 +42,7 @@ import java.util.Random; @LargeTest @RunWith(AndroidJUnit4.class) -public class MeasuredTextPerfTest { +public class PrecomputedTextPerfTest { private static final int WORD_LENGTH = 9; // Random word has 9 characters. private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line. private static final boolean NO_STYLE_TEXT = false; @@ -51,7 +51,7 @@ public class MeasuredTextPerfTest { private static TextPaint PAINT = new TextPaint(); private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); - public MeasuredTextPerfTest() {} + public PrecomputedTextPerfTest() {} @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); @@ -66,120 +66,136 @@ public class MeasuredTextPerfTest { @Test public void testCreate_NoStyled_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(true /* do full layout */); + PrecomputedText.create(text, param); } } @Test public void testCreate_NoStyled_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(true /* do full layout */); + PrecomputedText.create(text, param); } } @Test public void testCreate_NoStyled_Hyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(false /* width only */); + PrecomputedText.create(text, param); } } @Test public void testCreate_NoStyled_NoHyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(false /* width only */); + PrecomputedText.create(text, param); } } @Test public void testCreate_Styled_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(true /* do full layout */); + PrecomputedText.create(text, param); } } @Test public void testCreate_Styled_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(true /* do full layout */); + PrecomputedText.create(text, param); } } @Test public void testCreate_Styled_Hyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(false /* width only */); + PrecomputedText.create(text, param); } } @Test public void testCreate_Styled_NoHyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); + while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - new MeasuredText.Builder(text, PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(false /* width only */); + PrecomputedText.create(text, param); } } } diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java new file mode 100644 index 0000000000000000000000000000000000000000..60c6d89b39cefaa250f8437198493fdc61b52d7f --- /dev/null +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018 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 android.text; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.TimeUnit; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class StaticLayoutMultithreadPerfTest { + private static final int WORD_LENGTH = 9; // Random word has 9 characters. + private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line. + private static final boolean NO_STYLE_TEXT = false; + + private static TextPaint PAINT = new TextPaint(); + private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); + + public StaticLayoutMultithreadPerfTest() {} + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private CountDownLatch mStartLatch; + private AtomicBoolean mThreadState; // True for running, False for stopped. + + private static final long TIMEOUT_MS = 5000; + + private Thread[] startBackgroundThread(int numOfThreads) { + mStartLatch = new CountDownLatch(numOfThreads); + mThreadState = new AtomicBoolean(true); + + Thread[] threads = new Thread[numOfThreads]; + for (int i = 0; i < numOfThreads; ++i) { + final int seed = i + 1; + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + final TextPerfUtils util = new TextPerfUtils(); + util.resetRandom(seed); + + mStartLatch.countDown(); + while (mThreadState.get()) { + final CharSequence text = util.nextRandomParagraph( + WORD_LENGTH, NO_STYLE_TEXT); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .build(); + } + } + }); + } + + for (int i = 0; i < numOfThreads; ++i) { + threads[i].start(); + } + + try { + mStartLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return threads; + } + + private void finishThreads(Thread[] threads) { + mThreadState.set(false); + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + mStartLatch = null; + mThreadState = null; + } + + private void runRandomTest(int numOfTotalThreads) { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final TextPerfUtils util = new TextPerfUtils(); + Thread[] threads = startBackgroundThread(numOfTotalThreads - 1); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = util.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .build(); + } + finishThreads(threads); + } + + @Test + public void testCreate_RandomText_Thread_1() { + runRandomTest(1); + } + + @Test + public void testCreate_RandomText_Thread_2() { + runRandomTest(2); + } + + @Test + public void testCreate_RandomText_Thread_4() { + runRandomTest(4); + } +} diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 231aaf2ca074910077c537e634322594a47a8b7f..e1a38a0956d70177a1ccf6374133f55b0c94b4e7 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -63,6 +63,18 @@ public class StaticLayoutPerfTest { mTextUtil.resetRandom(0 /* seed */); } + private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) { + PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build(); + return PrecomputedText.create(text, param); + } + + private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy, + int frequency) { + PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint) + .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build(); + return PrecomputedText.create(text, param); + } + @Test public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); @@ -151,15 +163,13 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() { + public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, + Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -170,15 +180,13 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() { + public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, + Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -189,15 +197,13 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() { + public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, + Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -208,15 +214,13 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() { + public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, + Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -227,15 +231,13 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() { + public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, + Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -328,13 +330,13 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_MeasuredText_Styled() { + public void testDraw_PrecomputedText_Styled() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -345,13 +347,13 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_MeasuredText_NoStyled() { + public void testDraw_PrecomputedText_NoStyled() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -362,13 +364,13 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_MeasuredText_Styled_WithoutCache() { + public void testDraw_PrecomputedText_Styled_WithoutCache() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -380,13 +382,13 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_MeasuredText_NoStyled_WithoutCache() { + public void testDraw_PrecomputedText_NoStyled_WithoutCache() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + final PrecomputedText text = makeMeasured( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java index 2c84db18ce54c09676191015c05204b1c0d00a7c..40778de4e521554d7fd25e93d39f81e453b876f4 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java @@ -150,6 +150,8 @@ public final class ManualBenchmarkState { final Bundle status = new Bundle(); status.putLong(key + "_median", mStats.getMedian()); status.putLong(key + "_mean", (long) mStats.getMean()); + status.putLong(key + "_percentile90", mStats.getPercentile90()); + status.putLong(key + "_percentile95", mStats.getPercentile95()); status.putLong(key + "_stddev", (long) mStats.getStandardDeviation()); instrumentation.sendStatus(Activity.RESULT_OK, status); } diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java index acc44a8febfcfcf2feb17d40c4436c99209a46ee..5e50073c06744cab7fb9f8a135e05b313994679e 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.List; public class Stats { - private long mMedian, mMin, mMax; + private long mMedian, mMin, mMax, mPercentile90, mPercentile95; private double mMean, mStandardDeviation; /* Calculate stats in constructor. */ @@ -35,12 +35,14 @@ public class Stats { Collections.sort(values); - mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 : - values.get(size / 2); - mMin = values.get(0); mMax = values.get(values.size() - 1); + mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 : + values.get(size / 2); + mPercentile90 = getPercentile(values, 90); + mPercentile95 = getPercentile(values, 95); + for (int i = 0; i < size; ++i) { long result = values.get(i); mMean += result; @@ -73,4 +75,21 @@ public class Stats { public double getStandardDeviation() { return mStandardDeviation; } + + public long getPercentile90() { + return mPercentile90; + } + + public long getPercentile95() { + return mPercentile95; + } + + private static long getPercentile(List values, int percentile) { + if (percentile < 0 || percentile > 100) { + throw new IllegalArgumentException( + "invalid percentile " + percentile + ", should be 0-100"); + } + int idx = (values.size() - 1) * percentile / 100; + return values.get(idx); + } } diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java index 6012f4b12b3d3c41ef12a0c99efc4d52e800bfd9..8f03f7eea5841e2ef7f030557e5a0c8e05111466 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java @@ -17,6 +17,14 @@ package android.perftests.utils; import android.app.Activity; +import android.content.Context; +import android.content.Intent; public class StubActivity extends Activity { -} \ No newline at end of file + public static Intent createLaunchIntent(Context context) { + final Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(context, StubActivity.class); + return intent; + } +} diff --git a/api/current.txt b/api/current.txt index 3c3cfccaebef34593464986d318809a3cbab6220..fff502a577db85abf992a9620a93c5b8375493e8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1468,6 +1468,7 @@ package android { field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 + field public static final int urlBarResourceId = 16844164; // 0x1010584 field public static final int use32bitAbi = 16844053; // 0x1010515 field public static final int useDefaultMargins = 16843641; // 0x1010379 field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 @@ -2215,34 +2216,34 @@ package android { field public static final int Theme_DeviceDefault_Wallpaper = 16974140; // 0x103013c field public static final int Theme_DeviceDefault_Wallpaper_NoTitleBar = 16974141; // 0x103013d field public static final int Theme_Dialog = 16973835; // 0x103000b - field public static final int Theme_Holo = 16973931; // 0x103006b - field public static final int Theme_Holo_Dialog = 16973935; // 0x103006f - field public static final int Theme_Holo_DialogWhenLarge = 16973943; // 0x1030077 - field public static final int Theme_Holo_DialogWhenLarge_NoActionBar = 16973944; // 0x1030078 - field public static final int Theme_Holo_Dialog_MinWidth = 16973936; // 0x1030070 - field public static final int Theme_Holo_Dialog_NoActionBar = 16973937; // 0x1030071 - field public static final int Theme_Holo_Dialog_NoActionBar_MinWidth = 16973938; // 0x1030072 - field public static final int Theme_Holo_InputMethod = 16973951; // 0x103007f - field public static final int Theme_Holo_Light = 16973934; // 0x103006e - field public static final int Theme_Holo_Light_DarkActionBar = 16974105; // 0x1030119 - field public static final int Theme_Holo_Light_Dialog = 16973939; // 0x1030073 - field public static final int Theme_Holo_Light_DialogWhenLarge = 16973945; // 0x1030079 - field public static final int Theme_Holo_Light_DialogWhenLarge_NoActionBar = 16973946; // 0x103007a - field public static final int Theme_Holo_Light_Dialog_MinWidth = 16973940; // 0x1030074 - field public static final int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075 - field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076 - field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0 - field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1 - field public static final int Theme_Holo_Light_NoActionBar_Overscan = 16974302; // 0x10301de - field public static final int Theme_Holo_Light_NoActionBar_TranslucentDecor = 16974306; // 0x10301e2 - field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c - field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c - field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d - field public static final int Theme_Holo_NoActionBar_Overscan = 16974301; // 0x10301dd - field public static final int Theme_Holo_NoActionBar_TranslucentDecor = 16974305; // 0x10301e1 - field public static final int Theme_Holo_Panel = 16973947; // 0x103007b - field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d - field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e + field public static final deprecated int Theme_Holo = 16973931; // 0x103006b + field public static final deprecated int Theme_Holo_Dialog = 16973935; // 0x103006f + field public static final deprecated int Theme_Holo_DialogWhenLarge = 16973943; // 0x1030077 + field public static final deprecated int Theme_Holo_DialogWhenLarge_NoActionBar = 16973944; // 0x1030078 + field public static final deprecated int Theme_Holo_Dialog_MinWidth = 16973936; // 0x1030070 + field public static final deprecated int Theme_Holo_Dialog_NoActionBar = 16973937; // 0x1030071 + field public static final deprecated int Theme_Holo_Dialog_NoActionBar_MinWidth = 16973938; // 0x1030072 + field public static final deprecated int Theme_Holo_InputMethod = 16973951; // 0x103007f + field public static final deprecated int Theme_Holo_Light = 16973934; // 0x103006e + field public static final deprecated int Theme_Holo_Light_DarkActionBar = 16974105; // 0x1030119 + field public static final deprecated int Theme_Holo_Light_Dialog = 16973939; // 0x1030073 + field public static final deprecated int Theme_Holo_Light_DialogWhenLarge = 16973945; // 0x1030079 + field public static final deprecated int Theme_Holo_Light_DialogWhenLarge_NoActionBar = 16973946; // 0x103007a + field public static final deprecated int Theme_Holo_Light_Dialog_MinWidth = 16973940; // 0x1030074 + field public static final deprecated int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075 + field public static final deprecated int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076 + field public static final deprecated int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0 + field public static final deprecated int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1 + field public static final deprecated int Theme_Holo_Light_NoActionBar_Overscan = 16974302; // 0x10301de + field public static final deprecated int Theme_Holo_Light_NoActionBar_TranslucentDecor = 16974306; // 0x10301e2 + field public static final deprecated int Theme_Holo_Light_Panel = 16973948; // 0x103007c + field public static final deprecated int Theme_Holo_NoActionBar = 16973932; // 0x103006c + field public static final deprecated int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d + field public static final deprecated int Theme_Holo_NoActionBar_Overscan = 16974301; // 0x10301dd + field public static final deprecated int Theme_Holo_NoActionBar_TranslucentDecor = 16974305; // 0x10301e1 + field public static final deprecated int Theme_Holo_Panel = 16973947; // 0x103007b + field public static final deprecated int Theme_Holo_Wallpaper = 16973949; // 0x103007d + field public static final deprecated int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e field public static final int Theme_InputMethod = 16973908; // 0x1030054 field public static final int Theme_Light = 16973836; // 0x103000c field public static final int Theme_Light_NoTitleBar = 16973837; // 0x103000d @@ -3695,7 +3696,7 @@ package android.app { method public boolean onCreateOptionsMenu(android.view.Menu); method public boolean onCreatePanelMenu(int, android.view.Menu); method public android.view.View onCreatePanelView(int); - method public boolean onCreateThumbnail(android.graphics.Bitmap, android.graphics.Canvas); + method public deprecated boolean onCreateThumbnail(android.graphics.Bitmap, android.graphics.Canvas); method public android.view.View onCreateView(java.lang.String, android.content.Context, android.util.AttributeSet); method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet); method protected void onDestroy(); @@ -4069,7 +4070,7 @@ package android.app { method public android.app.ActivityOptions setAppVerificationBundle(android.os.Bundle); method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect); method public android.app.ActivityOptions setLaunchDisplayId(int); - method public android.app.ActivityOptions setLockTaskMode(boolean); + method public android.app.ActivityOptions setLockTaskEnabled(boolean); method public android.os.Bundle toBundle(); method public void update(android.app.ActivityOptions); field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time"; @@ -5311,6 +5312,7 @@ package android.app { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5 + field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4 field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2 field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3 @@ -5347,18 +5349,18 @@ package android.app { ctor public Notification.Action.WearableExtender(android.app.Notification.Action); method public android.app.Notification.Action.WearableExtender clone(); method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder); - method public java.lang.CharSequence getCancelLabel(); - method public java.lang.CharSequence getConfirmLabel(); + method public deprecated java.lang.CharSequence getCancelLabel(); + method public deprecated java.lang.CharSequence getConfirmLabel(); method public boolean getHintDisplayActionInline(); method public boolean getHintLaunchesActivity(); - method public java.lang.CharSequence getInProgressLabel(); + method public deprecated java.lang.CharSequence getInProgressLabel(); method public boolean isAvailableOffline(); method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean); - method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence); - method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence); + method public deprecated android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence); + method public deprecated android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence); method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean); method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean); - method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence); + method public deprecated android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence); } public static class Notification.BigPictureStyle extends android.app.Notification.Style { @@ -5581,39 +5583,39 @@ package android.app { method public android.graphics.Bitmap getBackground(); method public java.lang.String getBridgeTag(); method public int getContentAction(); - method public int getContentIcon(); - method public int getContentIconGravity(); + method public deprecated int getContentIcon(); + method public deprecated int getContentIconGravity(); method public boolean getContentIntentAvailableOffline(); - method public int getCustomContentHeight(); - method public int getCustomSizePreset(); + method public deprecated int getCustomContentHeight(); + method public deprecated int getCustomSizePreset(); method public java.lang.String getDismissalId(); method public android.app.PendingIntent getDisplayIntent(); - method public int getGravity(); + method public deprecated int getGravity(); method public boolean getHintAmbientBigPicture(); - method public boolean getHintAvoidBackgroundClipping(); + method public deprecated boolean getHintAvoidBackgroundClipping(); method public boolean getHintContentIntentLaunchesActivity(); - method public boolean getHintHideIcon(); - method public int getHintScreenTimeout(); - method public boolean getHintShowBackgroundOnly(); + method public deprecated boolean getHintHideIcon(); + method public deprecated int getHintScreenTimeout(); + method public deprecated boolean getHintShowBackgroundOnly(); method public java.util.List getPages(); method public boolean getStartScrollBottom(); method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap); method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String); method public android.app.Notification.WearableExtender setContentAction(int); - method public android.app.Notification.WearableExtender setContentIcon(int); - method public android.app.Notification.WearableExtender setContentIconGravity(int); + method public deprecated android.app.Notification.WearableExtender setContentIcon(int); + method public deprecated android.app.Notification.WearableExtender setContentIconGravity(int); method public android.app.Notification.WearableExtender setContentIntentAvailableOffline(boolean); - method public android.app.Notification.WearableExtender setCustomContentHeight(int); - method public android.app.Notification.WearableExtender setCustomSizePreset(int); + method public deprecated android.app.Notification.WearableExtender setCustomContentHeight(int); + method public deprecated android.app.Notification.WearableExtender setCustomSizePreset(int); method public android.app.Notification.WearableExtender setDismissalId(java.lang.String); method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent); - method public android.app.Notification.WearableExtender setGravity(int); + method public deprecated android.app.Notification.WearableExtender setGravity(int); method public android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean); - method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean); + method public deprecated android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean); method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean); - method public android.app.Notification.WearableExtender setHintHideIcon(boolean); - method public android.app.Notification.WearableExtender setHintScreenTimeout(int); - method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean); + method public deprecated android.app.Notification.WearableExtender setHintHideIcon(boolean); + method public deprecated android.app.Notification.WearableExtender setHintScreenTimeout(int); + method public deprecated android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean); method public android.app.Notification.WearableExtender setStartScrollBottom(boolean); field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0 @@ -6493,7 +6495,6 @@ package android.app.admin { method public boolean isNetworkLoggingEnabled(android.content.ComponentName); method public boolean isOverrideApnEnabled(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; - method public boolean isPrintingEnabled(); method public boolean isProfileOwnerApp(java.lang.String); method public boolean isProvisioningAllowed(java.lang.String); method public boolean isResetPasswordTokenActive(android.content.ComponentName); @@ -6566,7 +6567,6 @@ package android.app.admin { method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List); method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List); method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List); - method public void setPrintingEnabled(android.content.ComponentName, boolean); method public void setProfileEnabled(android.content.ComponentName); method public void setProfileName(android.content.ComponentName, java.lang.String); method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo); @@ -6595,7 +6595,7 @@ package android.app.admin { method public void uninstallCaCert(android.content.ComponentName, byte[]); method public boolean updateOverrideApn(android.content.ComponentName, int, android.telephony.data.ApnSetting); method public void wipeData(int); - method public void wipeDataWithReason(int, java.lang.CharSequence); + method public void wipeData(int, java.lang.CharSequence); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; @@ -6678,7 +6678,7 @@ package android.app.admin { field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20 field public static final int LOCK_TASK_FEATURE_NONE = 0; // 0x0 field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2 - field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8 + field public static final int LOCK_TASK_FEATURE_OVERVIEW = 8; // 0x8 field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1 field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2 field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; @@ -6707,6 +6707,7 @@ package android.app.admin { field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3 field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1 field public static final int USER_OPERATION_SUCCESS = 0; // 0x0 + field public static final int WIPE_EUICC = 4; // 0x4 field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1 field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2 } @@ -6741,6 +6742,8 @@ package android.app.admin { field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455 field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e + field public static final int TAG_CERT_VALIDATION_FAILURE = 210033; // 0x33471 + field public static final int TAG_CRYPTO_SELF_TEST_COMPLETED = 210031; // 0x3346f field public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET = 210021; // 0x33465 field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456 field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457 @@ -6748,6 +6751,7 @@ package android.app.admin { field public static final int TAG_KEY_DESTRUCTION = 210026; // 0x3346a field public static final int TAG_KEY_GENERATED = 210024; // 0x33468 field public static final int TAG_KEY_IMPORT = 210025; // 0x33469 + field public static final int TAG_KEY_INTEGRITY_VIOLATION = 210032; // 0x33470 field public static final int TAG_LOGGING_STARTED = 210011; // 0x3345b field public static final int TAG_LOGGING_STOPPED = 210012; // 0x3345c field public static final int TAG_LOG_BUFFER_SIZE_CRITICAL = 210015; // 0x3345f @@ -6874,6 +6878,7 @@ package android.app.assist { method public java.lang.String getIdEntry(); method public java.lang.String getIdPackage(); method public java.lang.String getIdType(); + method public int getImportantForAutofill(); method public int getInputType(); method public int getLeft(); method public android.os.LocaleList getLocaleList(); @@ -7262,6 +7267,7 @@ package android.app.slice { method public void registerSliceCallback(android.net.Uri, java.util.List, java.util.concurrent.Executor, android.app.slice.SliceManager.SliceCallback); method public void unpinSlice(android.net.Uri); method public void unregisterSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback); + field public static final java.lang.String SLICE_METADATA_KEY = "android.metadata.SLICE_URI"; } public static abstract interface SliceManager.SliceCallback { @@ -9441,6 +9447,7 @@ package android.content { field public static final java.lang.String DISPLAY_SERVICE = "display"; field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; + field public static final java.lang.String EUICC_SERVICE = "euicc"; field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; @@ -11266,6 +11273,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore"; field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony"; field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma"; + field public static final java.lang.String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc"; field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; field public static final java.lang.String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms"; field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television"; @@ -11382,6 +11390,8 @@ package android.content.pm { ctor public PermissionInfo(); ctor public PermissionInfo(android.content.pm.PermissionInfo); method public int describeContents(); + method public int getProtection(); + method public int getProtectionFlags(); method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager); field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_COSTS_MONEY = 1; // 0x1 @@ -11398,8 +11408,8 @@ package android.content.pm { field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800 field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10 field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200 - field public static final int PROTECTION_MASK_BASE = 15; // 0xf - field public static final int PROTECTION_MASK_FLAGS = 65520; // 0xfff0 + field public static final deprecated int PROTECTION_MASK_BASE = 15; // 0xf + field public static final deprecated int PROTECTION_MASK_FLAGS = 65520; // 0xfff0 field public static final int PROTECTION_NORMAL = 0; // 0x0 field public static final int PROTECTION_SIGNATURE = 2; // 0x2 field public static final deprecated int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3 @@ -11407,7 +11417,7 @@ package android.content.pm { field public int flags; field public java.lang.String group; field public java.lang.CharSequence nonLocalizedDescription; - field public int protectionLevel; + field public deprecated int protectionLevel; } public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable { @@ -12549,6 +12559,7 @@ package android.database.sqlite { method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int); method public void setIdleConnectionTimeout(long); method public void setLookasideConfig(int, int); + method public void setOpenParams(android.database.sqlite.SQLiteDatabase.OpenParams); method public void setWriteAheadLoggingEnabled(boolean); } @@ -13770,7 +13781,7 @@ package android.graphics { enum_constant public static final android.graphics.Matrix.ScaleToFit START; } - public class Movie { + public deprecated class Movie { method public static android.graphics.Movie decodeByteArray(byte[], int, int); method public static android.graphics.Movie decodeFile(java.lang.String); method public static android.graphics.Movie decodeStream(java.io.InputStream); @@ -13832,6 +13843,7 @@ package android.graphics { method public int breakText(java.lang.String, boolean, float, float[]); method public void clearShadowLayer(); method public float descent(); + method public boolean equalsForTextMeasurement(android.graphics.Paint); method public int getAlpha(); method public int getColor(); method public android.graphics.ColorFilter getColorFilter(); @@ -14498,7 +14510,9 @@ package android.graphics.drawable { ctor public AnimatedImageDrawable(); method public void clearAnimationCallbacks(); method public void draw(android.graphics.Canvas); + method public int getLoopCount(); method public int getOpacity(); + method public final boolean isAutoMirrored(); method public boolean isRunning(); method public void registerAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback); method public void setAlpha(int); @@ -14605,7 +14619,7 @@ package android.graphics.drawable { method public final android.graphics.Rect copyBounds(); method public static android.graphics.drawable.Drawable createFromPath(java.lang.String); method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String); - method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options); + method public static deprecated android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options); method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String); method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; @@ -15843,8 +15857,6 @@ package android.hardware.camera2 { method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract java.lang.String getId(); field public static final int TEMPLATE_MANUAL = 6; // 0x6 - field public static final int TEMPLATE_MOTION_TRACKING_BEST = 8; // 0x8 - field public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7; // 0x7 field public static final int TEMPLATE_PREVIEW = 1; // 0x1 field public static final int TEMPLATE_RECORD = 3; // 0x3 field public static final int TEMPLATE_STILL_CAPTURE = 2; // 0x2 @@ -16266,9 +16278,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_CORRECTION_MAP; field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP_MODE; field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_OIS_DATA_MODE; - field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_OIS_TIMESTAMPS; - field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_OIS_X_SHIFTS; - field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_OIS_Y_SHIFTS; + field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_OIS_SAMPLES; field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_SCENE_FLICKER; field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE; field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_GAMMA; @@ -16364,6 +16374,13 @@ package android.hardware.camera2.params { field public static final int METERING_WEIGHT_MIN = 0; // 0x0 } + public final class OisSample { + ctor public OisSample(long, float, float); + method public long getTimestamp(); + method public float getXshift(); + method public float getYshift(); + } + public final class OutputConfiguration implements android.os.Parcelable { ctor public OutputConfiguration(android.view.Surface); ctor public OutputConfiguration(int, android.view.Surface); @@ -16565,7 +16582,7 @@ package android.hardware.fingerprint { field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8 } - public static abstract class FingerprintManager.AuthenticationCallback { + public static abstract deprecated class FingerprintManager.AuthenticationCallback { ctor public FingerprintManager.AuthenticationCallback(); method public void onAuthenticationError(int, java.lang.CharSequence); method public void onAuthenticationFailed(); @@ -16573,11 +16590,11 @@ package android.hardware.fingerprint { method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintManager.AuthenticationResult); } - public static class FingerprintManager.AuthenticationResult { + public static deprecated class FingerprintManager.AuthenticationResult { method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject(); } - public static final class FingerprintManager.CryptoObject { + public static final deprecated class FingerprintManager.CryptoObject { ctor public FingerprintManager.CryptoObject(java.security.Signature); ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher); ctor public FingerprintManager.CryptoObject(javax.crypto.Mac); @@ -21896,6 +21913,7 @@ package android.media { field public static final int TYPE_FM_TUNER = 16; // 0x10 field public static final int TYPE_HDMI = 9; // 0x9 field public static final int TYPE_HDMI_ARC = 10; // 0xa + field public static final int TYPE_HEARING_AID = 23; // 0x17 field public static final int TYPE_IP = 20; // 0x14 field public static final int TYPE_LINE_ANALOG = 5; // 0x5 field public static final int TYPE_LINE_DIGITAL = 6; // 0x6 @@ -22824,6 +22842,27 @@ package android.media { field public static final int STOP_VIDEO_RECORDING = 3; // 0x3 } + public class MediaBrowser2 extends android.media.MediaController2 { + ctor public MediaBrowser2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaBrowser2.BrowserCallback); + method public void getChildren(java.lang.String, int, int, android.os.Bundle); + method public void getItem(java.lang.String); + method public void getLibraryRoot(android.os.Bundle); + method public void getSearchResult(java.lang.String, int, int, android.os.Bundle); + method public void search(java.lang.String, android.os.Bundle); + method public void subscribe(java.lang.String, android.os.Bundle); + method public void unsubscribe(java.lang.String); + } + + public static class MediaBrowser2.BrowserCallback extends android.media.MediaController2.ControllerCallback { + ctor public MediaBrowser2.BrowserCallback(); + method public void onChildrenChanged(java.lang.String, int, android.os.Bundle); + method public void onGetChildrenDone(java.lang.String, int, int, java.util.List, android.os.Bundle); + method public void onGetItemDone(java.lang.String, android.media.MediaItem2); + method public void onGetLibraryRootDone(android.os.Bundle, java.lang.String, android.os.Bundle); + method public void onGetSearchResultDone(java.lang.String, int, int, java.util.List, android.os.Bundle); + method public void onSearchResultChanged(java.lang.String, int, android.os.Bundle); + } + public final class MediaCas implements java.lang.AutoCloseable { ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException; method public void close(); @@ -23300,6 +23339,73 @@ package android.media { field public static final int REGULAR_CODECS = 0; // 0x0 } + public class MediaController2 implements java.lang.AutoCloseable { + ctor public MediaController2(android.content.Context, android.media.SessionToken2, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback); + method public void addPlaylistItem(int, android.media.MediaItem2); + method public void adjustVolume(int, int); + method public void close(); + method public void fastForward(); + method public long getBufferedPosition(); + method public android.media.MediaItem2 getCurrentPlaylistItem(); + method public android.media.MediaController2.PlaybackInfo getPlaybackInfo(); + method public float getPlaybackSpeed(); + method public int getPlayerState(); + method public java.util.List getPlaylist(); + method public android.media.MediaSession2.PlaylistParams getPlaylistParams(); + method public long getPosition(); + method public android.app.PendingIntent getSessionActivity(); + method public android.media.SessionToken2 getSessionToken(); + method public boolean isConnected(); + method public void pause(); + method public void play(); + method public void playFromMediaId(java.lang.String, android.os.Bundle); + method public void playFromSearch(java.lang.String, android.os.Bundle); + method public void playFromUri(android.net.Uri, android.os.Bundle); + method public void prepare(); + method public void prepareFromMediaId(java.lang.String, android.os.Bundle); + method public void prepareFromSearch(java.lang.String, android.os.Bundle); + method public void prepareFromUri(android.net.Uri, android.os.Bundle); + method public void removePlaylistItem(android.media.MediaItem2); + method public void rewind(); + method public void seekTo(long); + method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver); + method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams); + method public void setRating(java.lang.String, android.media.Rating2); + method public void setVolumeTo(int, int); + method public void skipToNext(); + method public void skipToPlaylistItem(android.media.MediaItem2); + method public void skipToPrevious(); + method public void stop(); + } + + public static abstract class MediaController2.ControllerCallback { + ctor public MediaController2.ControllerCallback(); + method public void onAllowedCommandsChanged(android.media.MediaSession2.CommandGroup); + method public void onBufferedPositionChanged(long); + method public void onConnected(android.media.MediaSession2.CommandGroup); + method public void onCurrentPlaylistItemChanged(android.media.MediaItem2); + method public void onCustomCommand(android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver); + method public void onCustomLayoutChanged(java.util.List); + method public void onDisconnected(); + method public void onError(int, int); + method public void onPlaybackInfoChanged(android.media.MediaController2.PlaybackInfo); + method public void onPlaybackSpeedChanged(float); + method public void onPlayerStateChanged(int); + method public void onPlaylistChanged(java.util.List); + method public void onPlaylistParamsChanged(android.media.MediaSession2.PlaylistParams); + method public void onPositionUpdated(long, long); + } + + public static final class MediaController2.PlaybackInfo { + method public android.media.AudioAttributes getAudioAttributes(); + method public int getControlType(); + method public int getCurrentVolume(); + method public int getMaxVolume(); + method public int getPlaybackType(); + field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1 + field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2 + } + public final class MediaCrypto { ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException; method protected void finalize(); @@ -23371,6 +23477,7 @@ package android.media { method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String); method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap) throws android.media.NotProvisionedException; method public int getMaxHdcpLevel(); + method public static int getMaxSecurityLevel(); method public int getMaxSessionCount(); method public android.os.PersistableBundle getMetrics(); method public int getOpenSessionCount(); @@ -23384,6 +23491,7 @@ package android.media { method public static boolean isCryptoSchemeSupported(java.util.UUID); method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String); method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException; + method public byte[] openSession(int) throws android.media.NotProvisionedException, android.media.ResourceBusyException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException; method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException; method public java.util.HashMap queryKeyStatus(byte[]); @@ -23399,7 +23507,6 @@ package android.media { method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler); method public void setPropertyByteArray(java.lang.String, byte[]); method public void setPropertyString(java.lang.String, java.lang.String); - method public void setSecurityLevel(byte[], int); field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3 field public static final int EVENT_KEY_REQUIRED = 2; // 0x2 field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1 @@ -23609,6 +23716,7 @@ package android.media { field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6 field public static final java.lang.String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level"; field public static final java.lang.String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level"; + field public static final java.lang.String KEY_AAC_DRC_EFFECT_TYPE = "aac-drc-effect-type"; field public static final java.lang.String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression"; field public static final java.lang.String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level"; field public static final java.lang.String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level"; @@ -23693,6 +23801,69 @@ package android.media { field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; } + public class MediaItem2 { + method public static android.media.MediaItem2 fromBundle(android.content.Context, android.os.Bundle); + method public android.media.DataSourceDesc getDataSourceDesc(); + method public int getFlags(); + method public java.lang.String getMediaId(); + method public android.media.MediaMetadata2 getMetadata(); + method public boolean isBrowsable(); + method public boolean isPlayable(); + method public void setMetadata(android.media.MediaMetadata2); + method public android.os.Bundle toBundle(); + field public static final int FLAG_BROWSABLE = 1; // 0x1 + field public static final int FLAG_PLAYABLE = 2; // 0x2 + } + + public static final class MediaItem2.Builder { + ctor public MediaItem2.Builder(android.content.Context, int); + method public android.media.MediaItem2 build(); + method public android.media.MediaItem2.Builder setDataSourceDesc(android.media.DataSourceDesc); + method public android.media.MediaItem2.Builder setMediaId(java.lang.String); + method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata2); + } + + public abstract class MediaLibraryService2 extends android.media.MediaSessionService2 { + ctor public MediaLibraryService2(); + method public abstract android.media.MediaLibraryService2.MediaLibrarySession onCreateSession(java.lang.String); + field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaLibraryService2"; + } + + public static final class MediaLibraryService2.LibraryRoot { + ctor public MediaLibraryService2.LibraryRoot(android.content.Context, java.lang.String, android.os.Bundle); + method public android.os.Bundle getExtras(); + method public java.lang.String getRootId(); + field public static final java.lang.String EXTRA_OFFLINE = "android.media.extra.OFFLINE"; + field public static final java.lang.String EXTRA_RECENT = "android.media.extra.RECENT"; + field public static final java.lang.String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED"; + } + + public static final class MediaLibraryService2.MediaLibrarySession extends android.media.MediaSession2 { + method public void notifyChildrenChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle); + method public void notifyChildrenChanged(java.lang.String, int, android.os.Bundle); + method public void notifySearchResultChanged(android.media.MediaSession2.ControllerInfo, java.lang.String, int, android.os.Bundle); + } + + public static final class MediaLibraryService2.MediaLibrarySession.Builder { + ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, android.media.MediaPlayerBase, java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback); + method public android.media.MediaLibraryService2.MediaLibrarySession build(); + method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setId(java.lang.String); + method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent); + method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback); + method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setVolumeProvider(android.media.VolumeProvider2); + } + + public static class MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback extends android.media.MediaSession2.SessionCallback { + ctor public MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback(android.content.Context); + method public java.util.List onGetChildren(android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle); + method public android.media.MediaItem2 onGetItem(android.media.MediaSession2.ControllerInfo, java.lang.String); + method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaSession2.ControllerInfo, android.os.Bundle); + method public java.util.List onGetSearchResult(android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle); + method public void onSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onSubscribe(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onUnsubscribe(android.media.MediaSession2.ControllerInfo, java.lang.String); + } + public final class MediaMetadata implements android.os.Parcelable { method public boolean containsKey(java.lang.String); method public int describeContents(); @@ -23748,6 +23919,79 @@ package android.media { method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence); } + public final class MediaMetadata2 { + method public boolean containsKey(java.lang.String); + method public static android.media.MediaMetadata2 fromBundle(android.content.Context, android.os.Bundle); + method public android.graphics.Bitmap getBitmap(java.lang.String); + method public android.os.Bundle getExtras(); + method public float getFloat(java.lang.String); + method public long getLong(java.lang.String); + method public java.lang.String getMediaId(); + method public android.media.Rating2 getRating(java.lang.String); + method public java.lang.String getString(java.lang.String); + method public java.lang.CharSequence getText(java.lang.String); + method public java.util.Set keySet(); + method public int size(); + method public android.os.Bundle toBundle(); + field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L + field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L + field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L + field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L + field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L + field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L + field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L + field public static final java.lang.String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT"; + field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"; + field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"; + field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; + field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"; + field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART"; + field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"; + field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"; + field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"; + field public static final java.lang.String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE"; + field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"; + field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"; + field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE"; + field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; + field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION"; + field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON"; + field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI"; + field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE"; + field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"; + field public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS"; + field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION"; + field public static final java.lang.String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS"; + field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE"; + field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"; + field public static final java.lang.String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI"; + field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"; + field public static final java.lang.String METADATA_KEY_RADIO_CALLSIGN = "android.media.metadata.RADIO_CALLSIGN"; + field public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY = "android.media.metadata.RADIO_FREQUENCY"; + field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING"; + field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE"; + field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; + field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"; + field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER"; + field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR"; + field public static final long STATUS_DOWNLOADED = 2L; // 0x2L + field public static final long STATUS_DOWNLOADING = 1L; // 0x1L + field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L + } + + public static final class MediaMetadata2.Builder { + ctor public MediaMetadata2.Builder(android.content.Context); + ctor public MediaMetadata2.Builder(android.content.Context, android.media.MediaMetadata2); + method public android.media.MediaMetadata2 build(); + method public android.media.MediaMetadata2.Builder putBitmap(java.lang.String, android.graphics.Bitmap); + method public android.media.MediaMetadata2.Builder putFloat(java.lang.String, float); + method public android.media.MediaMetadata2.Builder putLong(java.lang.String, long); + method public android.media.MediaMetadata2.Builder putRating(java.lang.String, android.media.Rating2); + method public android.media.MediaMetadata2.Builder putString(java.lang.String, java.lang.String); + method public android.media.MediaMetadata2.Builder putText(java.lang.String, java.lang.CharSequence); + method public android.media.MediaMetadata2.Builder setExtras(android.os.Bundle); + } + public abstract deprecated class MediaMetadataEditor { method public synchronized void addEditableKey(int); method public abstract void apply(); @@ -24063,13 +24307,14 @@ package android.media { method public static final android.media.MediaPlayer2 create(); method public abstract void deselectTrack(int); method public abstract android.media.DataSourceDesc editPlaylistItem(int, android.media.DataSourceDesc); + method public abstract android.media.AudioAttributes getAudioAttributes(); method public abstract int getAudioSessionId(); method public abstract android.media.DataSourceDesc getCurrentDataSource(); method public abstract int getCurrentPlaylistItemIndex(); - method public abstract int getCurrentPosition(); + method public abstract long getCurrentPosition(); method public abstract android.media.MediaPlayer2.DrmInfo getDrmInfo(); method public abstract java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException; - method public abstract int getDuration(); + method public abstract long getDuration(); method public abstract android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map) throws android.media.MediaPlayer2.NoDrmSchemeException; method public abstract int getLoopingMode(); method public abstract android.os.PersistableBundle getMetrics(); @@ -24155,8 +24400,8 @@ package android.media { public static abstract class MediaPlayer2.DrmEventCallback { ctor public MediaPlayer2.DrmEventCallback(); - method public void onDrmInfo(android.media.MediaPlayer2, android.media.MediaPlayer2.DrmInfo); - method public void onDrmPrepared(android.media.MediaPlayer2, int); + method public void onDrmInfo(android.media.MediaPlayer2, long, android.media.MediaPlayer2.DrmInfo); + method public void onDrmPrepared(android.media.MediaPlayer2, long, int); } public static abstract class MediaPlayer2.DrmInfo { @@ -24194,7 +24439,7 @@ package android.media { } public static abstract interface MediaPlayer2.OnDrmConfigHelper { - method public abstract void onDrmConfig(android.media.MediaPlayer2); + method public abstract void onDrmConfig(android.media.MediaPlayer2, long); } public static abstract class MediaPlayer2.ProvisioningNetworkErrorException extends android.media.MediaDrmException { @@ -24218,6 +24463,19 @@ package android.media { field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1 } + public abstract class MediaPlayerBase implements java.lang.AutoCloseable { + ctor public MediaPlayerBase(); + method public abstract android.media.AudioAttributes getAudioAttributes(); + method public abstract int getPlayerState(); + method public abstract void pause(); + method public abstract void play(); + method public abstract void setAudioAttributes(android.media.AudioAttributes); + field public static final int STATE_ERROR = 0; // 0x0 + field public static final int STATE_IDLE = 0; // 0x0 + field public static final int STATE_PAUSED = 0; // 0x0 + field public static final int STATE_PLAYING = 0; // 0x0 + } + public class MediaRecorder implements android.media.AudioRouting { ctor public MediaRecorder(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); @@ -24494,6 +24752,172 @@ package android.media { method public abstract void onScanCompleted(java.lang.String, android.net.Uri); } + public class MediaSession2 implements java.lang.AutoCloseable { + method public void addPlaylistItem(int, android.media.MediaItem2); + method public void close(); + method public void editPlaylistItem(android.media.MediaItem2); + method public void fastForward(); + method public java.util.List getConnectedControllers(); + method public android.media.MediaItem2 getCurrentPlaylistItem(); + method public android.media.MediaPlayerBase getPlayer(); + method public java.util.List getPlaylist(); + method public android.media.MediaSession2.PlaylistParams getPlaylistParams(); + method public android.media.SessionToken2 getToken(); + method public void notifyError(int, int); + method public void pause(); + method public void play(); + method public void prepare(); + method public void removePlaylistItem(android.media.MediaItem2); + method public void rewind(); + method public void seekTo(long); + method public void sendCustomCommand(android.media.MediaSession2.Command, android.os.Bundle); + method public void sendCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver); + method public void setAllowedCommands(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.CommandGroup); + method public void setCustomLayout(android.media.MediaSession2.ControllerInfo, java.util.List); + method public void setPlayer(android.media.MediaPlayerBase); + method public void setPlayer(android.media.MediaPlayerBase, android.media.VolumeProvider2); + method public void setPlaylist(java.util.List); + method public void setPlaylistParams(android.media.MediaSession2.PlaylistParams); + method public void skipToNext(); + method public void skipToPlaylistItem(android.media.MediaItem2); + method public void skipToPrevious(); + method public void stop(); + field public static final int COMMAND_CODE_BROWSER = 22; // 0x16 + field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0 + field public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7; // 0x7 + field public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2; // 0x2 + field public static final int COMMAND_CODE_PLAYBACK_PLAY = 1; // 0x1 + field public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6; // 0x6 + field public static final int COMMAND_CODE_PLAYBACK_REWIND = 8; // 0x8 + field public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9; // 0x9 + field public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10; // 0xa + field public static final int COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS = 11; // 0xb + field public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4; // 0x4 + field public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5; // 0x5 + field public static final int COMMAND_CODE_PLAYBACK_STOP = 3; // 0x3 + field public static final int COMMAND_CODE_PLAYLIST_ADD = 12; // 0xc + field public static final int COMMAND_CODE_PLAYLIST_GET = 14; // 0xe + field public static final int COMMAND_CODE_PLAYLIST_REMOVE = 13; // 0xd + field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 16; // 0x10 + field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 18; // 0x12 + field public static final int COMMAND_CODE_PLAY_FROM_URI = 17; // 0x11 + field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 19; // 0x13 + field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 21; // 0x15 + field public static final int COMMAND_CODE_PREPARE_FROM_URI = 20; // 0x14 + field public static final int COMMAND_CODE_SET_VOLUME = 15; // 0xf + field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa + field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1 + field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3 + field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5 + field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8 + field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb + field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7 + field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2 + field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6 + field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4 + field public static final int ERROR_CODE_SETUP_REQUIRED = 12; // 0xc + field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9 + field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0 + } + + public static final class MediaSession2.Builder { + ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase); + method public android.media.MediaSession2 build(); + method public android.media.MediaSession2.Builder setId(java.lang.String); + method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent); + method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback); + method public android.media.MediaSession2.Builder setVolumeProvider(android.media.VolumeProvider2); + } + + public static final class MediaSession2.Command { + ctor public MediaSession2.Command(android.content.Context, int); + ctor public MediaSession2.Command(android.content.Context, java.lang.String, android.os.Bundle); + method public int getCommandCode(); + method public java.lang.String getCustomCommand(); + method public android.os.Bundle getExtra(); + } + + public static final class MediaSession2.CommandButton { + method public android.media.MediaSession2.Command getCommand(); + method public java.lang.String getDisplayName(); + method public android.os.Bundle getExtra(); + method public int getIconResId(); + method public boolean isEnabled(); + } + + public static final class MediaSession2.CommandButton.Builder { + ctor public MediaSession2.CommandButton.Builder(android.content.Context); + method public android.media.MediaSession2.CommandButton build(); + method public android.media.MediaSession2.CommandButton.Builder setCommand(android.media.MediaSession2.Command); + method public android.media.MediaSession2.CommandButton.Builder setDisplayName(java.lang.String); + method public android.media.MediaSession2.CommandButton.Builder setEnabled(boolean); + method public android.media.MediaSession2.CommandButton.Builder setExtra(android.os.Bundle); + method public android.media.MediaSession2.CommandButton.Builder setIconResId(int); + } + + public static final class MediaSession2.CommandGroup { + ctor public MediaSession2.CommandGroup(android.content.Context); + ctor public MediaSession2.CommandGroup(android.content.Context, android.media.MediaSession2.CommandGroup); + method public void addAllPredefinedCommands(); + method public void addCommand(android.media.MediaSession2.Command); + method public boolean hasCommand(android.media.MediaSession2.Command); + method public boolean hasCommand(int); + method public void removeCommand(android.media.MediaSession2.Command); + } + + public static final class MediaSession2.ControllerInfo { + method public java.lang.String getPackageName(); + method public int getUid(); + method public boolean isTrusted(); + } + + public static final class MediaSession2.PlaylistParams { + ctor public MediaSession2.PlaylistParams(android.content.Context, int, int, android.media.MediaMetadata2); + method public static android.media.MediaSession2.PlaylistParams fromBundle(android.content.Context, android.os.Bundle); + method public android.media.MediaMetadata2 getPlaylistMetadata(); + method public int getRepeatMode(); + method public int getShuffleMode(); + method public android.os.Bundle toBundle(); + field public static final int REPEAT_MODE_ALL = 2; // 0x2 + field public static final int REPEAT_MODE_GROUP = 3; // 0x3 + field public static final int REPEAT_MODE_NONE = 0; // 0x0 + field public static final int REPEAT_MODE_ONE = 1; // 0x1 + field public static final int SHUFFLE_MODE_ALL = 1; // 0x1 + field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2 + field public static final int SHUFFLE_MODE_NONE = 0; // 0x0 + } + + public static abstract class MediaSession2.SessionCallback { + ctor public MediaSession2.SessionCallback(android.content.Context); + method public boolean onCommandRequest(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command); + method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2.ControllerInfo); + method public void onCustomCommand(android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver); + method public void onDisconnected(android.media.MediaSession2.ControllerInfo); + method public void onPlayFromMediaId(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPlayFromSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPlayFromUri(android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle); + method public void onPrepareFromMediaId(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPrepareFromSearch(android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPrepareFromUri(android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle); + method public void onSetRating(android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2); + } + + public abstract class MediaSessionService2 extends android.app.Service { + ctor public MediaSessionService2(); + method public final android.media.MediaSession2 getSession(); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract android.media.MediaSession2 onCreateSession(java.lang.String); + method public android.media.MediaSessionService2.MediaNotification onUpdateNotification(); + field public static final java.lang.String SERVICE_INTERFACE = "android.media.MediaSessionService2"; + field public static final java.lang.String SERVICE_META_DATA = "android.media.session"; + } + + public static class MediaSessionService2.MediaNotification { + ctor public MediaSessionService2.MediaNotification(android.content.Context, int, android.app.Notification); + method public android.app.Notification getNotification(); + method public int getNotificationId(); + } + public final class MediaSync { ctor public MediaSync(); method public android.view.Surface createInputSurface(); @@ -24561,13 +24985,19 @@ package android.media { field public static final int DIRECTIONALITY_OMNI = 1; // 0x1 field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5 field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0 + field public static final int GROUP_UNKNOWN = -1; // 0xffffffff + field public static final int INDEX_IN_THE_GROUP_UNKNOWN = -1; // 0xffffffff field public static final int LOCATION_MAINBODY = 1; // 0x1 field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2 field public static final int LOCATION_PERIPHERAL = 3; // 0x3 field public static final int LOCATION_UNKNOWN = 0; // 0x0 + field public static final android.media.MicrophoneInfo.Coordinate3F ORIENTATION_UNKNOWN; + field public static final android.media.MicrophoneInfo.Coordinate3F POSITION_UNKNOWN; + field public static final float SENSITIVITY_UNKNOWN = -3.4028235E38f; + field public static final float SPL_UNKNOWN = -3.4028235E38f; } - public class MicrophoneInfo.Coordinate3F { + public static final class MicrophoneInfo.Coordinate3F { field public final float x; field public final float y; field public final float z; @@ -24618,6 +25048,29 @@ package android.media { field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2 } + public final class Rating2 { + method public static android.media.Rating2 fromBundle(android.content.Context, android.os.Bundle); + method public float getPercentRating(); + method public int getRatingStyle(); + method public float getStarRating(); + method public boolean hasHeart(); + method public boolean isRated(); + method public boolean isThumbUp(); + method public static android.media.Rating2 newHeartRating(android.content.Context, boolean); + method public static android.media.Rating2 newPercentageRating(android.content.Context, float); + method public static android.media.Rating2 newStarRating(android.content.Context, int, float); + method public static android.media.Rating2 newThumbRating(android.content.Context, boolean); + method public static android.media.Rating2 newUnratedRating(android.content.Context, int); + method public android.os.Bundle toBundle(); + field public static final int RATING_3_STARS = 3; // 0x3 + field public static final int RATING_4_STARS = 4; // 0x4 + field public static final int RATING_5_STARS = 5; // 0x5 + field public static final int RATING_HEART = 1; // 0x1 + field public static final int RATING_NONE = 0; // 0x0 + field public static final int RATING_PERCENTAGE = 6; // 0x6 + field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2 + } + public deprecated class RemoteControlClient { ctor public RemoteControlClient(android.app.PendingIntent); ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper); @@ -24753,6 +25206,22 @@ package android.media { field public static final int URI_COLUMN_INDEX = 2; // 0x2 } + public final class SessionToken2 { + ctor public SessionToken2(android.content.Context, java.lang.String, java.lang.String); + method public static android.media.SessionToken2 fromBundle(android.content.Context, android.os.Bundle); + method public java.lang.String getId(); + method public java.lang.String getPackageName(); + method public int getType(); + method public int getUid(); + method public android.os.Bundle toBundle(); + field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2 + field public static final int TYPE_SESSION = 0; // 0x0 + field public static final int TYPE_SESSION_SERVICE = 1; // 0x1 + } + + public static abstract class SessionToken2.TokenType implements java.lang.annotation.Annotation { + } + public class SoundPool { ctor public deprecated SoundPool(int, int, int); method public final void autoPause(); @@ -24956,6 +25425,19 @@ package android.media { field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1 } + public abstract class VolumeProvider2 { + ctor public VolumeProvider2(android.content.Context, int, int, int); + method public final int getControlType(); + method public final int getCurrentVolume(); + method public final int getMaxVolume(); + method public void onAdjustVolume(int); + method public void onSetVolumeTo(int); + method public final void setCurrentVolume(int); + field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2 + field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0 + field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1 + } + public final class VolumeShaper implements java.lang.AutoCloseable { method public void apply(android.media.VolumeShaper.Operation); method public void close(); @@ -25702,14 +26184,23 @@ package android.media.session { public final class MediaSessionManager { method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName); method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler); + method public void addOnSessionTokensChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnSessionTokensChangedListener); + method public java.util.List getActiveSessionTokens(); method public java.util.List getActiveSessions(android.content.ComponentName); + method public java.util.List getAllSessionTokens(); + method public java.util.List getSessionServiceTokens(); method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener); + method public void removeOnSessionTokensChangedListener(android.media.session.MediaSessionManager.OnSessionTokensChangedListener); } public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener { method public abstract void onActiveSessionsChanged(java.util.List); } + public static abstract interface MediaSessionManager.OnSessionTokensChangedListener { + method public abstract void onSessionTokensChanged(java.util.List); + } + public final class PlaybackState implements android.os.Parcelable { method public int describeContents(); method public long getActions(); @@ -26750,7 +27241,7 @@ package android.net { field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL"; field public static final java.lang.String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED"; - field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; + field public static final deprecated java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL"; field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; @@ -27026,6 +27517,7 @@ package android.net { field public static final int NET_CAPABILITY_CBS = 5; // 0x5 field public static final int NET_CAPABILITY_DUN = 2; // 0x2 field public static final int NET_CAPABILITY_EIMS = 10; // 0xa + field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13 field public static final int NET_CAPABILITY_FOTA = 3; // 0x3 field public static final int NET_CAPABILITY_IA = 7; // 0x7 field public static final int NET_CAPABILITY_IMS = 4; // 0x4 @@ -27034,6 +27526,7 @@ package android.net { field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12 + field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15 field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf field public static final int NET_CAPABILITY_RCS = 8; // 0x8 field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 @@ -27990,23 +28483,23 @@ package android.net.wifi { method public int updateNetwork(android.net.wifi.WifiConfiguration); field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; - field public static final int ERROR_AUTHENTICATING = 1; // 0x1 + field public static final deprecated int ERROR_AUTHENTICATING = 1; // 0x1 field public static final deprecated java.lang.String EXTRA_BSSID = "bssid"; field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; - field public static final java.lang.String EXTRA_NEW_STATE = "newState"; + field public static final deprecated java.lang.String EXTRA_NEW_STATE = "newState"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated"; - field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected"; - field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError"; + field public static final deprecated java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected"; + field public static final deprecated java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError"; field public static final deprecated java.lang.String EXTRA_WIFI_INFO = "wifiInfo"; field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state"; field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED"; field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; - field public static final java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; - field public static final java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; + field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"; + field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE"; field public static final int WIFI_MODE_FULL = 1; // 0x1 field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3 field public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2 @@ -28061,20 +28554,21 @@ package android.net.wifi { method public abstract deprecated void onSucceeded(); } - public class WpsInfo implements android.os.Parcelable { - ctor public WpsInfo(); - ctor public WpsInfo(android.net.wifi.WpsInfo); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public java.lang.String BSSID; - field public static final android.os.Parcelable.Creator CREATOR; - field public static final int DISPLAY = 1; // 0x1 - field public static final int INVALID = 4; // 0x4 - field public static final int KEYPAD = 2; // 0x2 - field public static final int LABEL = 3; // 0x3 - field public static final int PBC = 0; // 0x0 - field public java.lang.String pin; - field public int setup; + public deprecated class WpsInfo implements android.os.Parcelable { + ctor public deprecated WpsInfo(); + ctor public deprecated WpsInfo(android.net.wifi.WpsInfo); + method public deprecated int describeContents(); + method public deprecated java.lang.String toString(); + method public deprecated void writeToParcel(android.os.Parcel, int); + field public deprecated java.lang.String BSSID; + field public static final deprecated android.os.Parcelable.Creator CREATOR; + field public static final deprecated int DISPLAY = 1; // 0x1 + field public static final deprecated int INVALID = 4; // 0x4 + field public static final deprecated int KEYPAD = 2; // 0x2 + field public static final deprecated int LABEL = 3; // 0x3 + field public static final deprecated int PBC = 0; // 0x0 + field public deprecated java.lang.String pin; + field public deprecated int setup; } } @@ -28500,29 +28994,6 @@ package android.net.wifi.p2p.nsd { package android.net.wifi.rtt { - public final class LocationCivic implements android.os.Parcelable { - method public int describeContents(); - method public byte[] getData(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; - } - - public final class LocationConfigurationInformation implements android.os.Parcelable { - method public int describeContents(); - method public double getAltitude(); - method public int getAltitudeType(); - method public double getAltitudeUncertainty(); - method public double getLatitude(); - method public double getLatitudeUncertainty(); - method public double getLongitude(); - method public double getLongitudeUncertainty(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int ALTITUDE_IN_FLOORS = 2; // 0x2 - field public static final int ALTITUDE_IN_METERS = 1; // 0x1 - field public static final int ALTITUDE_UNKNOWN = 0; // 0x0 - field public static final android.os.Parcelable.Creator CREATOR; - } - public final class RangingRequest implements android.os.Parcelable { method public int describeContents(); method public static int getMaxPeers(); @@ -28545,14 +29016,13 @@ package android.net.wifi.rtt { method public int getDistanceStdDevMm(); method public android.net.MacAddress getMacAddress(); method public android.net.wifi.aware.PeerHandle getPeerHandle(); - method public long getRangingTimestampUs(); - method public android.net.wifi.rtt.LocationCivic getReportedLocationCivic(); - method public android.net.wifi.rtt.LocationConfigurationInformation getReportedLocationConfigurationInformation(); + method public long getRangingTimestampMillis(); method public int getRssi(); method public int getStatus(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int STATUS_FAIL = 1; // 0x1 + field public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2; // 0x2 field public static final int STATUS_SUCCESS = 0; // 0x0 } @@ -28566,7 +29036,7 @@ package android.net.wifi.rtt { public class WifiRttManager { method public boolean isAvailable(); - method public void startRanging(android.net.wifi.rtt.RangingRequest, android.net.wifi.rtt.RangingResultCallback, android.os.Handler); + method public void startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback); field public static final java.lang.String ACTION_WIFI_RTT_STATE_CHANGED = "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED"; } @@ -32989,7 +33459,7 @@ package android.os { method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog(); method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox(); method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen(); - method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor); + method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnThreadViolationListener); method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog(); method public android.os.StrictMode.ThreadPolicy.Builder permitAll(); method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls(); @@ -33021,20 +33491,20 @@ package android.os { method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure(); method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox(); - method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor); + method public android.os.StrictMode.VmPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnVmViolationListener); method public android.os.StrictMode.VmPolicy.Builder penaltyLog(); method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int); } public final class SystemClock { + method public static java.time.Clock currentNetworkTimeClock(); + method public static long currentNetworkTimeMillis(); method public static long currentThreadTimeMillis(); method public static long elapsedRealtime(); - method public static java.time.Clock elapsedRealtimeClock(); method public static long elapsedRealtimeNanos(); method public static boolean setCurrentTimeMillis(long); method public static void sleep(long); method public static long uptimeMillis(); - method public static java.time.Clock uptimeMillisClock(); } public class TestLooperManager { @@ -33140,6 +33610,7 @@ package android.os { field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset"; field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam"; field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls"; + field public static final java.lang.String DISALLOW_PRINTING = "no_printing"; field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile"; field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user"; field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot"; @@ -34630,10 +35101,9 @@ package android.provider { field public static final java.lang.String DURATION = "duration"; field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER"; field public static final java.lang.String FEATURES = "features"; - field public static final int FEATURES_ASSISTED_DIALING_USED = 16; // 0x10 field public static final int FEATURES_HD_CALL = 4; // 0x4 field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2 - field public static final int FEATURES_RTT = 32; // 0x20 + field public static final int FEATURES_RTT = 16; // 0x10 field public static final int FEATURES_VIDEO = 1; // 0x1 field public static final int FEATURES_WIFI = 8; // 0x8 field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location"; @@ -36630,6 +37100,7 @@ package android.provider { field public static final deprecated java.lang.String RADIO_NFC = "nfc"; field public static final deprecated java.lang.String RADIO_WIFI = "wifi"; field public static final java.lang.String RINGTONE = "ringtone"; + field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode"; field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness"; field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1 @@ -36773,6 +37244,13 @@ package android.provider { field public static final java.lang.String ADDRESS = "address"; } + public static final class Telephony.CarrierIdentification implements android.provider.BaseColumns { + method public static android.net.Uri getUriForSubscriptionId(int); + field public static final java.lang.String CID = "carrier_id"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String NAME = "carrier_name"; + } + public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final java.lang.String APN = "apn"; field public static final java.lang.String AUTH_TYPE = "authtype"; @@ -38246,17 +38724,6 @@ package android.se.omapi { method public byte[] transmit(byte[]) throws java.io.IOException; } - public abstract interface ISecureElementListener implements android.os.IInterface { - method public abstract void serviceConnected() throws android.os.RemoteException; - } - - public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener { - ctor public ISecureElementListener.Stub(); - method public android.os.IBinder asBinder(); - method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder); - method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException; - } - public class Reader { method public void closeSessions(); method public java.lang.String getName(); @@ -38266,13 +38733,19 @@ package android.se.omapi { } public class SEService { - ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener); + ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener); method public android.se.omapi.Reader[] getReaders(); method public java.lang.String getVersion(); method public boolean isConnected(); method public void shutdown(); } + public static abstract class SEService.SecureElementListener extends android.os.Binder { + ctor public SEService.SecureElementListener(); + method public android.os.IBinder asBinder(); + method public void serviceConnected(); + } + public class Session { method public void close(); method public void closeChannels(); @@ -38670,14 +39143,14 @@ package android.service.autofill { } public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation { - ctor public DateTransformation(android.view.autofill.AutofillId, java.text.DateFormat); + ctor public DateTransformation(android.view.autofill.AutofillId, android.icu.text.DateFormat); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } public final class DateValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer { - ctor public DateValueSanitizer(java.text.DateFormat); + ctor public DateValueSanitizer(android.icu.text.DateFormat); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -38688,7 +39161,7 @@ package android.service.autofill { } public static final class FieldClassification.Match { - method public java.lang.String getRemoteId(); + method public java.lang.String getCategoryId(); method public float getScore(); } @@ -38853,6 +39326,7 @@ package android.service.autofill { method public int describeContents(); method public java.lang.String getFieldClassificationAlgorithm(); method public java.lang.String getId(); + method public static int getMaxCategoryCount(); method public static int getMaxFieldClassificationIdsSize(); method public static int getMaxUserDataSize(); method public static int getMaxValueLength(); @@ -40505,6 +40979,7 @@ package android.telecom { public final class Call { method public void answer(int); method public void conference(android.telecom.Call); + method public void deflect(android.net.Uri); method public void disconnect(); method public java.util.List getCannedTextResponses(); method public java.util.List getChildren(); @@ -40615,9 +41090,9 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00 field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400 field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 + field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 - field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 field public static final int PROPERTY_CONFERENCE = 1; // 0x1 field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 @@ -40625,6 +41100,7 @@ package android.telecom { field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 + field public static final int PROPERTY_RTT = 1024; // 0x400 field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -40718,8 +41194,8 @@ package android.telecom { method public final void setActive(); method public final void setConferenceableConnections(java.util.List); method public final void setConnectionCapabilities(int); - method public final void setConnectionElapsedTime(long); method public final void setConnectionProperties(int); + method public final void setConnectionStartElapsedRealTime(long); method public final void setConnectionTime(long); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); @@ -40762,6 +41238,7 @@ package android.telecom { method public void onAnswer(); method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onCallEvent(java.lang.String, android.os.Bundle); + method public void onDeflect(android.net.Uri); method public void onDisconnect(); method public void onExtrasChanged(android.os.Bundle); method public void onHandoverComplete(); @@ -40830,6 +41307,7 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00 field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400 field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 + field public static final int CAPABILITY_SUPPORT_DEFLECT = 33554432; // 0x2000000 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED"; @@ -40839,7 +41317,6 @@ package android.telecom { field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; - field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int PROPERTY_IS_RTT = 256; // 0x100 @@ -41256,14 +41733,12 @@ package android.telecom { field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL"; field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED"; field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED"; - field public static final java.lang.String ACTION_SHOW_ASSISTED_DIALING_SETTINGS = "android.telecom.action.SHOW_ASSISTED_DIALING_SETTINGS"; field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS"; field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS"; field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION"; field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS"; field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ',' field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';' - field public static final java.lang.String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO = "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO"; field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER"; field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE"; field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE"; @@ -41279,7 +41754,6 @@ package android.telecom { field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; - field public static final java.lang.String EXTRA_USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE"; field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS"; @@ -41292,18 +41766,6 @@ package android.telecom { field public static final int PRESENTATION_UNKNOWN = 3; // 0x3 } - public final class TransformationInfo implements android.os.Parcelable { - ctor public TransformationInfo(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int); - method public int describeContents(); - method public java.lang.String getOriginalNumber(); - method public java.lang.String getTransformedNumber(); - method public int getTransformedNumberCountryCallingCode(); - method public java.lang.String getUserHomeCountryCode(); - method public java.lang.String getUserRoamingCountryCode(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; - } - public class VideoProfile implements android.os.Parcelable { ctor public VideoProfile(int); ctor public VideoProfile(int, int); @@ -41470,7 +41932,6 @@ package android.telephony { field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; - field public static final java.lang.String KEY_ASSISTED_DIALING_ENABLED_BOOL = "assisted_dialing_enabled_bool"; field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool"; field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array"; field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool"; @@ -41809,12 +42270,12 @@ package android.telephony { public class MbmsDownloadSession implements java.lang.AutoCloseable { method public int cancelDownload(android.telephony.mbms.DownloadRequest); method public void close(); - method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler); - method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler); + method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback); + method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback); method public int download(android.telephony.mbms.DownloadRequest); method public java.io.File getTempFileRootDirectory(); method public java.util.List listPendingDownloads(); - method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler); + method public int registerStateCallback(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStateCallback); method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo); method public void requestUpdateFileServices(java.util.List); method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest); @@ -41842,10 +42303,10 @@ package android.telephony { public class MbmsStreamingSession implements java.lang.AutoCloseable { method public void close(); - method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler); - method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler); + method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback); + method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsStreamingSessionCallback); method public void requestUpdateStreamingServices(java.util.List); - method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler); + method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback); } public class NeighboringCellInfo implements android.os.Parcelable { @@ -42193,13 +42654,16 @@ package android.telephony { method public java.lang.String getNumber(); method public int getSimSlotIndex(); method public int getSubscriptionId(); + method public boolean isEmbedded(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } public class SubscriptionManager { method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); + method public boolean canManageSubscription(android.telephony.SubscriptionInfo); method public static deprecated android.telephony.SubscriptionManager from(android.content.Context); + method public java.util.List getAccessibleSubscriptionInfoList(); method public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int); method public int getActiveSubscriptionInfoCount(); method public int getActiveSubscriptionInfoCountMax(); @@ -42584,6 +43048,44 @@ package android.telephony.data { } +package android.telephony.euicc { + + public final class DownloadableSubscription implements android.os.Parcelable { + method public int describeContents(); + method public static android.telephony.euicc.DownloadableSubscription forActivationCode(java.lang.String); + method public java.lang.String getConfirmationCode(); + method public java.lang.String getEncodedActivationCode(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class EuiccInfo implements android.os.Parcelable { + ctor public EuiccInfo(java.lang.String); + method public int describeContents(); + method public java.lang.String getOsVersion(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public class EuiccManager { + method public void deleteSubscription(int, android.app.PendingIntent); + method public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent); + method public java.lang.String getEid(); + method public android.telephony.euicc.EuiccInfo getEuiccInfo(); + method public boolean isEnabled(); + method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException; + method public void switchToSubscription(int, android.app.PendingIntent); + field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; + field public static final java.lang.String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE"; + field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2 + field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0 + field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1 + field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE"; + field public static final java.lang.String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon"; + } + +} + package android.telephony.gsm { public class GsmCellLocation extends android.telephony.CellLocation { @@ -42677,20 +43179,23 @@ package android.telephony.gsm { package android.telephony.mbms { public final class DownloadRequest implements android.os.Parcelable { - method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest); method public int describeContents(); + method public android.net.Uri getDestinationUri(); method public java.lang.String getFileServiceId(); method public static int getMaxAppIntentSize(); method public static int getMaxDestinationUriSize(); method public android.net.Uri getSourceUri(); method public int getSubscriptionId(); + method public byte[] toByteArray(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } public static class DownloadRequest.Builder { - ctor public DownloadRequest.Builder(android.net.Uri); + ctor public DownloadRequest.Builder(android.net.Uri, android.net.Uri); method public android.telephony.mbms.DownloadRequest build(); + method public static android.telephony.mbms.DownloadRequest.Builder fromDownloadRequest(android.telephony.mbms.DownloadRequest); + method public static android.telephony.mbms.DownloadRequest.Builder fromSerializedRequest(byte[]); method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent); method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo); method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int); @@ -42786,10 +43291,10 @@ package android.telephony.mbms { method public java.util.Date getSessionStartTime(); } - public class StreamingService { + public class StreamingService implements java.lang.AutoCloseable { + method public void close(); method public android.telephony.mbms.StreamingServiceInfo getInfo(); method public android.net.Uri getPlaybackUri(); - method public void stopStreaming(); field public static final int BROADCAST_METHOD = 1; // 0x1 field public static final int REASON_BY_USER_REQUEST = 1; // 0x1 field public static final int REASON_END_OF_SESSION = 2; // 0x2 @@ -43169,45 +43674,47 @@ package android.text { method public boolean isAllowed(char); } - public class MeasuredText implements android.text.Spanned { + public abstract interface NoCopySpan { + } + + public static class NoCopySpan.Concrete implements android.text.NoCopySpan { + ctor public NoCopySpan.Concrete(); + } + + public abstract interface ParcelableSpan implements android.os.Parcelable { + method public abstract int getSpanTypeId(); + } + + public class PrecomputedText implements android.text.Spanned { method public char charAt(int); - method public int getBreakStrategy(); - method public int getEnd(); - method public int getHyphenationFrequency(); - method public android.text.TextPaint getPaint(); + method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params); method public int getParagraphCount(); method public int getParagraphEnd(int); method public int getParagraphStart(int); + method public android.text.PrecomputedText.Params getParams(); method public int getSpanEnd(java.lang.Object); method public int getSpanFlags(java.lang.Object); method public int getSpanStart(java.lang.Object); method public T[] getSpans(int, int, java.lang.Class); - method public int getStart(); method public java.lang.CharSequence getText(); - method public android.text.TextDirectionHeuristic getTextDir(); method public int length(); method public int nextSpanTransition(int, int, java.lang.Class); method public java.lang.CharSequence subSequence(int, int); } - public static final class MeasuredText.Builder { - ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint); - method public android.text.MeasuredText build(); - method public android.text.MeasuredText.Builder setBreakStrategy(int); - method public android.text.MeasuredText.Builder setHyphenationFrequency(int); - method public android.text.MeasuredText.Builder setRange(int, int); - method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic); - } - - public abstract interface NoCopySpan { - } - - public static class NoCopySpan.Concrete implements android.text.NoCopySpan { - ctor public NoCopySpan.Concrete(); + public static final class PrecomputedText.Params { + method public int getBreakStrategy(); + method public int getHyphenationFrequency(); + method public android.text.TextDirectionHeuristic getTextDirection(); + method public android.text.TextPaint getTextPaint(); } - public abstract interface ParcelableSpan implements android.os.Parcelable { - method public abstract int getSpanTypeId(); + public static class PrecomputedText.Params.Builder { + ctor public PrecomputedText.Params.Builder(android.text.TextPaint); + method public android.text.PrecomputedText.Params build(); + method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int); + method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int); + method public android.text.PrecomputedText.Params.Builder setTextDirection(android.text.TextDirectionHeuristic); } public class Selection { @@ -44424,10 +44931,12 @@ package android.text.style { public class TypefaceSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan { ctor public TypefaceSpan(java.lang.String); + ctor public TypefaceSpan(android.graphics.Typeface); ctor public TypefaceSpan(android.os.Parcel); method public int describeContents(); method public java.lang.String getFamily(); method public int getSpanTypeId(); + method public android.graphics.Typeface getTypeface(); method public void updateDrawState(android.text.TextPaint); method public void updateMeasureState(android.text.TextPaint); method public void writeToParcel(android.os.Parcel, int); @@ -44898,6 +45407,7 @@ package android.util { method public abstract int getAttributeListValue(int, java.lang.String[], int); method public abstract java.lang.String getAttributeName(int); method public abstract int getAttributeNameResource(int); + method public default java.lang.String getAttributeNamespace(int); method public abstract int getAttributeResourceValue(java.lang.String, java.lang.String, int); method public abstract int getAttributeResourceValue(int, int); method public abstract int getAttributeUnsignedIntValue(java.lang.String, java.lang.String, int); @@ -47560,6 +48070,7 @@ package android.view { method public void setAlpha(float); method public void setAnimation(android.view.animation.Animation); method public void setAutofillHints(java.lang.String...); + method public void setAutofillId(android.view.autofill.AutofillId); method public void setBackground(android.graphics.drawable.Drawable); method public void setBackgroundColor(int); method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable); @@ -48147,8 +48658,6 @@ package android.view { method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect); method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect); - method protected void onDebugDraw(android.graphics.Canvas); - method protected void onDebugDrawMargins(android.graphics.Canvas, android.graphics.Paint); method public boolean onInterceptHoverEvent(android.view.MotionEvent); method public boolean onInterceptTouchEvent(android.view.MotionEvent); method protected abstract void onLayout(boolean, int, int, int, int); @@ -48222,7 +48731,6 @@ package android.view { ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet); ctor public ViewGroup.LayoutParams(int, int); ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams); - method public void onDebugDraw(android.view.View, android.graphics.Canvas, android.graphics.Paint); method public void resolveLayoutDirection(int); method protected void setBaseAttributes(android.content.res.TypedArray, int, int); field public static final deprecated int FILL_PARENT = -1; // 0xffffffff @@ -48410,6 +48918,7 @@ package android.view { method public abstract void setHint(java.lang.CharSequence); method public abstract void setHtmlInfo(android.view.ViewStructure.HtmlInfo); method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String); + method public void setImportantForAutofill(int); method public abstract void setInputType(int); method public abstract void setLocaleList(android.os.LocaleList); method public abstract void setLongClickable(boolean); @@ -49725,6 +50234,7 @@ package android.view.autofill { method public android.content.ComponentName getAutofillServiceComponentName(); method public java.util.List getAvailableFieldClassificationAlgorithms(); method public java.lang.String getDefaultFieldClassificationAlgorithm(); + method public android.view.autofill.AutofillId getNextAutofillId(); method public android.service.autofill.UserData getUserData(); method public java.lang.String getUserDataId(); method public boolean hasEnabledAutofillServices(); @@ -49733,6 +50243,8 @@ package android.view.autofill { method public boolean isFieldClassificationEnabled(); method public void notifyValueChanged(android.view.View); method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue); + method public void notifyViewClicked(android.view.View); + method public void notifyViewClicked(android.view.View, int); method public void notifyViewEntered(android.view.View); method public void notifyViewEntered(android.view.View, int, android.graphics.Rect); method public void notifyViewExited(android.view.View); @@ -50238,14 +50750,13 @@ package android.view.textclassifier { method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList); method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options); method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence); - method public default java.util.Collection getEntitiesForPreset(int); method public default android.view.textclassifier.logging.Logger getLogger(android.view.textclassifier.logging.Logger.Config); + method public default int getMaxGenerateLinksTextLength(); method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options); method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int); method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList); - field public static final int ENTITY_PRESET_ALL = 0; // 0x0 - field public static final int ENTITY_PRESET_BASE = 2; // 0x2 - field public static final int ENTITY_PRESET_NONE = 1; // 0x1 + field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable"; + field public static final java.lang.String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable"; field public static final android.view.textclassifier.TextClassifier NO_OP; field public static final java.lang.String TYPE_ADDRESS = "address"; field public static final java.lang.String TYPE_DATE = "date"; @@ -50259,11 +50770,12 @@ package android.view.textclassifier { } public static final class TextClassifier.EntityConfig implements android.os.Parcelable { - ctor public TextClassifier.EntityConfig(int); + method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection); + method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection, java.util.Collection, java.util.Collection); + method public static android.view.textclassifier.TextClassifier.EntityConfig createWithEntityList(java.util.Collection); method public int describeContents(); - method public android.view.textclassifier.TextClassifier.EntityConfig excludeEntities(java.lang.String...); - method public java.util.List getEntities(android.view.textclassifier.TextClassifier); - method public android.view.textclassifier.TextClassifier.EntityConfig includeEntities(java.lang.String...); + method public java.util.Collection getHints(); + method public java.util.List resolveEntityListModifications(java.util.Collection); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -50362,7 +50874,7 @@ package android.view.textclassifier.logging { method public final void logSelectionModifiedEvent(int, int); method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification); method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection); - method public final void logSelectionStartedEvent(int); + method public final void logSelectionStartedEvent(int, int); method public abstract void writeEvent(android.view.textclassifier.logging.SelectionEvent); field public static final int OUT_OF_BOUNDS = 2147483647; // 0x7fffffff field public static final int OUT_OF_BOUNDS_NEGATIVE = -2147483648; // 0x80000000 @@ -50392,6 +50904,7 @@ package android.view.textclassifier.logging { method public int getEventIndex(); method public long getEventTime(); method public int getEventType(); + method public int getInvocationMethod(); method public java.lang.String getPackageName(); method public java.lang.String getSessionId(); method public java.lang.String getSignature(); @@ -50416,6 +50929,8 @@ package android.view.textclassifier.logging { field public static final int EVENT_SELECTION_STARTED = 1; // 0x1 field public static final int EVENT_SMART_SELECTION_MULTI = 4; // 0x4 field public static final int EVENT_SMART_SELECTION_SINGLE = 3; // 0x3 + field public static final int INVOCATION_LINK = 2; // 0x2 + field public static final int INVOCATION_MANUAL = 1; // 0x1 } } @@ -50691,41 +51206,37 @@ package android.webkit { } public class TracingConfig { - ctor public TracingConfig(int); - ctor public TracingConfig(int, java.lang.String, int); - method public java.lang.String getCustomCategoryPattern(); - method public int getPresetCategories(); + method public java.util.List getCustomIncludedCategories(); + method public int getPredefinedCategories(); method public int getTracingMode(); - field public static final int CATEGORIES_FRAME_VIEWER = 4; // 0x4 - field public static final int CATEGORIES_INPUT_LATENCY = 1; // 0x1 - field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3; // 0x3 - field public static final int CATEGORIES_NONE = -1; // 0xffffffff - field public static final int CATEGORIES_RENDERING = 2; // 0x2 - field public static final int CATEGORIES_WEB_DEVELOPER = 0; // 0x0 + field public static final int CATEGORIES_ALL = 1; // 0x1 + field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2 + field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40 + field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8 + field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20 + field public static final int CATEGORIES_NONE = 0; // 0x0 + field public static final int CATEGORIES_RENDERING = 16; // 0x10 + field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4 field public static final int RECORD_CONTINUOUSLY = 1; // 0x1 - field public static final int RECORD_TO_CONSOLE = 3; // 0x3 field public static final int RECORD_UNTIL_FULL = 0; // 0x0 field public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; // 0x2 } + public static class TracingConfig.Builder { + ctor public TracingConfig.Builder(); + method public android.webkit.TracingConfig.Builder addCategories(int...); + method public android.webkit.TracingConfig.Builder addCategories(java.lang.String...); + method public android.webkit.TracingConfig.Builder addCategories(java.util.Collection); + method public android.webkit.TracingConfig build(); + method public android.webkit.TracingConfig.Builder setTracingMode(int); + } + public abstract class TracingController { ctor public TracingController(); method public static android.webkit.TracingController getInstance(); method public abstract boolean isTracing(); - method public abstract boolean start(android.webkit.TracingConfig); - method public abstract boolean stop(); - method public abstract boolean stopAndFlush(android.webkit.TracingController.TracingOutputStream, android.os.Handler); - } - - public static abstract interface TracingController.TracingOutputStream { - method public abstract void complete(); - method public abstract void write(byte[]); - } - - public class TracingFileOutputStream implements android.webkit.TracingController.TracingOutputStream { - ctor public TracingFileOutputStream(java.lang.String) throws java.io.FileNotFoundException; - method public void complete(); - method public void write(byte[]); + method public abstract void start(android.webkit.TracingConfig); + method public abstract boolean stop(java.io.OutputStream, java.util.concurrent.Executor); } public final class URLUtil { @@ -53590,6 +54101,7 @@ package android.widget { method public final android.content.res.ColorStateList getTextColors(); method public java.util.Locale getTextLocale(); method public android.os.LocaleList getTextLocales(); + method public android.text.PrecomputedText.Params getTextMetricsParams(); method public float getTextScaleX(); method public float getTextSize(); method public int getTotalPaddingBottom(); @@ -53719,6 +54231,7 @@ package android.widget { method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType); method public void setTextLocale(java.util.Locale); method public void setTextLocales(android.os.LocaleList); + method public void setTextMetricsParams(android.text.PrecomputedText.Params); method public void setTextScaleX(float); method public void setTextSize(float); method public void setTextSize(int, float); diff --git a/api/removed.txt b/api/removed.txt index 2aab223ed281d88936a26f30fa4c4c2cd2b83248..79c54fde547745940c9ebcf6c766220febf04dc7 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -257,6 +257,20 @@ package android.os { ctor public RecoverySystem(); } + public static final class StrictMode.ThreadPolicy.Builder { + method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor); + } + + public static final class StrictMode.VmPolicy.Builder { + method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor); + } + + public final class SystemClock { + method public static java.time.Clock elapsedRealtimeClock(); + method public static java.time.Clock uptimeClock(); + method public static deprecated java.time.Clock uptimeMillisClock(); + } + public class TestLooperManager { method public deprecated android.os.MessageQueue getQueue(); } diff --git a/api/system-current.txt b/api/system-current.txt index 2d3b65a19b26236300da322562692310a421916b..3aca59a5724d7f245d7c04f2a25c5d8668f49c11 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10,6 +10,7 @@ package android { field public static final java.lang.String ACCESS_MTP = "android.permission.ACCESS_MTP"; field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS"; field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS"; + field public static final java.lang.String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS"; field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; @@ -20,6 +21,7 @@ package android { field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE"; field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH"; + field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE"; field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE"; field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"; @@ -30,8 +32,8 @@ package android { field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"; field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE"; - field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE"; field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE"; + field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE"; field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED"; @@ -123,6 +125,7 @@ package android { field public static final java.lang.String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION"; field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; + field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; field public static final java.lang.String READ_FRAME_BUFFER = "android.permission.READ_FRAME_BUFFER"; @@ -178,6 +181,7 @@ package android { field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED"; field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE"; field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER"; + field public static final java.lang.String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS"; field public static final java.lang.String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS"; field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK"; @@ -186,6 +190,7 @@ package android { field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS"; field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE"; + field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"; field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES"; field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS"; @@ -199,6 +204,7 @@ package android { field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int requiredSystemPropertyName = 16844133; // 0x1010565 field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566 + field public static final int userRestriction = 16844165; // 0x1010585 } public static final class R.raw { @@ -227,7 +233,6 @@ package android.app { method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions); method public deprecated boolean isBackgroundVisibleBehind(); method public deprecated void onBackgroundVisibleBehindChanged(boolean); - method public void setDisablePreviewScreenshots(boolean); } public static abstract interface Activity.TranslucentConversionListener { @@ -341,11 +346,13 @@ package android.app { method public java.lang.String getChannelId(); method public android.app.PendingIntent getContentIntent(); method public android.app.PendingIntent getDeleteIntent(); + method public boolean getSuppressShowOverApps(); method public boolean isAvailableOnTv(); method public android.app.Notification.TvExtender setChannel(java.lang.String); method public android.app.Notification.TvExtender setChannelId(java.lang.String); method public android.app.Notification.TvExtender setContentIntent(android.app.PendingIntent); method public android.app.Notification.TvExtender setDeleteIntent(android.app.PendingIntent); + method public android.app.Notification.TvExtender setSuppressShowOverApps(boolean); } public final class NotificationChannel implements android.os.Parcelable { @@ -362,10 +369,12 @@ package android.app { public final class StatsManager { method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String); + method public boolean addConfiguration(long, byte[]); method public byte[] getData(long); method public byte[] getMetadata(); method public boolean removeConfiguration(long); method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); + method public boolean setDataFetchOperation(long, android.app.PendingIntent); field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY"; field public static final java.lang.String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID"; @@ -431,6 +440,19 @@ package android.app.admin { field public static final int STATE_USER_UNMANAGED = 0; // 0x0 } + public class SystemUpdatePolicy implements android.os.Parcelable { + method public int describeContents(); + method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TYPE_PAUSE = 4; // 0x4 + } + + public static class SystemUpdatePolicy.InstallationOption { + method public long getEffectiveTime(); + method public int getType(); + } + } package android.app.backup { @@ -449,7 +471,11 @@ package android.app.backup { method public android.app.backup.RestoreSession beginRestoreSession(); method public void cancelBackups(); method public long getAvailableRestoreToken(java.lang.String); + method public android.content.Intent getConfigurationIntent(java.lang.String); method public java.lang.String getCurrentTransport(); + method public android.content.Intent getDataManagementIntent(java.lang.String); + method public java.lang.String getDataManagementLabel(java.lang.String); + method public java.lang.String getDestinationString(java.lang.String); method public boolean isAppEligibleForBackup(java.lang.String); method public boolean isBackupEnabled(); method public boolean isBackupServiceActive(android.os.UserHandle); @@ -687,6 +713,12 @@ package android.app.usage { field public static final java.lang.String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService"; } + public static final class UsageEvents.Event { + method public int getStandbyBucket(); + field public static final int NOTIFICATION_SEEN = 10; // 0xa + field public static final int STANDBY_BUCKET_CHANGED = 11; // 0xb + } + public final class UsageStatsManager { method public int getAppStandbyBucket(java.lang.String); method public java.util.Map getAppStandbyBuckets(); @@ -783,6 +815,7 @@ package android.content { method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); field public static final java.lang.String BACKUP_SERVICE = "backup"; field public static final java.lang.String CONTEXTHUB_SERVICE = "contexthub"; + field public static final java.lang.String EUICC_CARD_SERVICE = "euicc_card"; field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score"; field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock"; @@ -791,7 +824,7 @@ package android.content { field public static final java.lang.String STATS_MANAGER = "stats"; field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final java.lang.String VR_SERVICE = "vrmanager"; - field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager"; + field public static final deprecated java.lang.String WIFI_RTT_SERVICE = "rttmanager"; field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner"; } @@ -821,6 +854,7 @@ package android.content { field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; + field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED"; field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST"; @@ -1059,6 +1093,7 @@ package android.content.pm { public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { field public static final int FLAG_REMOVED = 2; // 0x2 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 + field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public int requestRes; } @@ -2510,12 +2545,15 @@ package android.media { public class AudioManager { method public deprecated int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); + method public void clearAudioServerStateCallback(); method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy); + method public boolean isAudioServerRunning(); method public boolean isHdmiSystemAudioSupported(); method public int registerAudioPolicy(android.media.audiopolicy.AudioPolicy); method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method public deprecated int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; method public int requestAudioFocus(android.media.AudioFocusRequest, android.media.audiopolicy.AudioPolicy); + method public void setAudioServerStateCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioServerStateCallback); method public void setFocusRequestResult(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy); method public void unregisterAudioPolicyAsync(android.media.audiopolicy.AudioPolicy); field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1 @@ -2523,6 +2561,12 @@ package android.media { field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2 } + public static abstract class AudioManager.AudioServerStateCallback { + ctor public AudioManager.AudioServerStateCallback(); + method public void onAudioServerDown(); + method public void onAudioServerUp(); + } + public final class AudioPlaybackConfiguration implements android.os.Parcelable { method public int getClientPid(); method public int getClientUid(); @@ -3091,7 +3135,7 @@ package android.net.http { package android.net.wifi { - public class RttManager { + public deprecated class RttManager { method public void disableResponder(android.net.wifi.RttManager.ResponderCallback); method public void enableResponder(android.net.wifi.RttManager.ResponderCallback); method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities(); @@ -3167,22 +3211,22 @@ package android.net.wifi { field public int supportedType; } - public static class RttManager.ParcelableRttParams implements android.os.Parcelable { + public static deprecated class RttManager.ParcelableRttParams implements android.os.Parcelable { field public android.net.wifi.RttManager.RttParams[] mParams; } - public static class RttManager.ParcelableRttResults implements android.os.Parcelable { + public static deprecated class RttManager.ParcelableRttResults implements android.os.Parcelable { ctor public RttManager.ParcelableRttResults(android.net.wifi.RttManager.RttResult[]); field public android.net.wifi.RttManager.RttResult[] mResults; } - public static abstract class RttManager.ResponderCallback { + public static abstract deprecated class RttManager.ResponderCallback { ctor public RttManager.ResponderCallback(); method public abstract void onResponderEnableFailure(int); method public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig); } - public static class RttManager.ResponderConfig implements android.os.Parcelable { + public static deprecated class RttManager.ResponderConfig implements android.os.Parcelable { ctor public RttManager.ResponderConfig(); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -3195,7 +3239,7 @@ package android.net.wifi { field public int preamble; } - public static class RttManager.RttCapabilities implements android.os.Parcelable { + public static deprecated class RttManager.RttCapabilities implements android.os.Parcelable { ctor public RttManager.RttCapabilities(); field public int bwSupported; field public boolean lciSupported; @@ -3210,13 +3254,13 @@ package android.net.wifi { field public boolean twoSided11McRttSupported; } - public static abstract interface RttManager.RttListener { + public static abstract deprecated interface RttManager.RttListener { method public abstract void onAborted(); method public abstract void onFailure(int, java.lang.String); method public abstract void onSuccess(android.net.wifi.RttManager.RttResult[]); } - public static class RttManager.RttParams { + public static deprecated class RttManager.RttParams { ctor public RttManager.RttParams(); field public boolean LCIRequest; field public boolean LCRRequest; @@ -3240,7 +3284,7 @@ package android.net.wifi { field public boolean secure; } - public static class RttManager.RttResult { + public static deprecated class RttManager.RttResult { ctor public RttManager.RttResult(); field public android.net.wifi.RttManager.WifiInformationElement LCI; field public android.net.wifi.RttManager.WifiInformationElement LCR; @@ -3277,7 +3321,7 @@ package android.net.wifi { field public deprecated int tx_rate; } - public static class RttManager.WifiInformationElement { + public static deprecated class RttManager.WifiInformationElement { ctor public RttManager.WifiInformationElement(); field public byte[] data; field public byte id; @@ -3489,6 +3533,11 @@ package android.net.wifi.rtt { method public android.net.wifi.rtt.RangingRequest.Builder addResponder(android.net.wifi.rtt.ResponderConfig); } + public final class RangingResult implements android.os.Parcelable { + method public byte[] getLci(); + method public byte[] getLcr(); + } + public final class ResponderConfig implements android.os.Parcelable { ctor public ResponderConfig(android.net.MacAddress, int, boolean, int, int, int, int, int); ctor public ResponderConfig(android.net.wifi.aware.PeerHandle, int, boolean, int, int, int, int, int); @@ -3524,7 +3573,7 @@ package android.net.wifi.rtt { public class WifiRttManager { method public void cancelRanging(android.os.WorkSource); - method public void startRanging(android.os.WorkSource, android.net.wifi.rtt.RangingRequest, android.net.wifi.rtt.RangingResultCallback, android.os.Handler); + method public void startRanging(android.os.WorkSource, android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback); } } @@ -3570,11 +3619,14 @@ package android.os { } public abstract class HwBinder implements android.os.IHwBinder { + ctor public HwBinder(); method public static final void configureRpcThreadpool(long, boolean); method public static void enableInstrumentation(); method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException; method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException; method public static final void joinRpcThreadpool(); + method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; + method public final void registerService(java.lang.String) throws android.os.RemoteException; method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; } @@ -3797,6 +3849,7 @@ package android.os { method public void resume(); method public void suspend(); method public boolean unbind(); + method public boolean verifyPayloadMetadata(java.lang.String); } public static final class UpdateEngine.ErrorCodeConstants { @@ -4124,6 +4177,11 @@ package android.provider { method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean); method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages"; + field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names"; + field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist"; + field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; + field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent"; + field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis"; field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on"; field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; @@ -4135,6 +4193,7 @@ package android.provider { method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean); method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; + field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count"; field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size"; field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size"; field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; @@ -4215,7 +4274,7 @@ package android.security.keystore.recovery { method public int getMaxAttempts(); method public byte[] getServerParams(); method public int getSnapshotVersion(); - method public byte[] getTrustedHardwarePublicKey(); + method public java.security.cert.CertPath getTrustedHardwareCertPath(); method public java.util.List getWrappedApplicationKeys(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -4236,21 +4295,21 @@ package android.security.keystore.recovery { } public class RecoveryController { + method public android.security.keystore.recovery.RecoverySession createRecoverySession(); method public byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; - method public java.util.List getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; + method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; + method public java.util.List getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException; method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context); method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException; - method public android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException; method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException; - method public int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; + method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; method public void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException; method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException; method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException; - method public void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException; + method public void setRecoveryStatus(java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException; method public void setServerParams(byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException; method public void setSnapshotCreatedPendingIntent(android.app.PendingIntent) throws android.security.keystore.recovery.InternalRecoveryServiceException; - field public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2; // 0x2 field public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3; // 0x3 field public static final int RECOVERY_STATUS_SYNCED = 0; // 0x0 field public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1; // 0x1 @@ -4259,7 +4318,7 @@ package android.security.keystore.recovery { public class RecoverySession implements java.lang.AutoCloseable { method public void close(); method public java.util.Map recoverKeys(byte[], java.util.List) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException; - method public byte[] start(byte[], byte[], byte[], java.util.List) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException; + method public byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException; } public class SessionExpiredException extends java.security.GeneralSecurityException { @@ -4268,7 +4327,6 @@ package android.security.keystore.recovery { public final class WrappedApplicationKey implements android.os.Parcelable { method public int describeContents(); - method public byte[] getAccount(); method public java.lang.String getAlias(); method public byte[] getEncryptedKeyMaterial(); method public void writeToParcel(android.os.Parcel, int); @@ -4278,7 +4336,6 @@ package android.security.keystore.recovery { public static class WrappedApplicationKey.Builder { ctor public WrappedApplicationKey.Builder(); method public android.security.keystore.recovery.WrappedApplicationKey build(); - method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]); method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String); method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]); } @@ -4297,6 +4354,125 @@ package android.service.autofill { } +package android.service.euicc { + + public final class EuiccProfileInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.service.carrier.CarrierIdentifier getCarrierIdentifier(); + method public java.lang.String getIccid(); + method public java.lang.String getNickname(); + method public int getPolicyRules(); + method public int getProfileClass(); + method public java.lang.String getProfileName(); + method public java.lang.String getServiceProviderName(); + method public int getState(); + method public java.util.List getUiccAccessRules(); + method public boolean hasPolicyRule(int); + method public boolean hasPolicyRules(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int POLICY_RULE_DELETE_AFTER_DISABLING = 4; // 0x4 + field public static final int POLICY_RULE_DO_NOT_DELETE = 2; // 0x2 + field public static final int POLICY_RULE_DO_NOT_DISABLE = 1; // 0x1 + field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 + field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1 + field public static final int PROFILE_CLASS_TESTING = 0; // 0x0 + field public static final int PROFILE_STATE_DISABLED = 0; // 0x0 + field public static final int PROFILE_STATE_ENABLED = 1; // 0x1 + } + + public static final class EuiccProfileInfo.Builder { + ctor public EuiccProfileInfo.Builder(java.lang.String); + ctor public EuiccProfileInfo.Builder(android.service.euicc.EuiccProfileInfo); + method public android.service.euicc.EuiccProfileInfo build(); + method public android.service.euicc.EuiccProfileInfo.Builder setCarrierIdentifier(android.service.carrier.CarrierIdentifier); + method public android.service.euicc.EuiccProfileInfo.Builder setIccid(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setNickname(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setPolicyRules(int); + method public android.service.euicc.EuiccProfileInfo.Builder setProfileClass(int); + method public android.service.euicc.EuiccProfileInfo.Builder setProfileName(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setServiceProviderName(java.lang.String); + method public android.service.euicc.EuiccProfileInfo.Builder setState(int); + method public android.service.euicc.EuiccProfileInfo.Builder setUiccAccessRule(java.util.List); + } + + public static abstract class EuiccProfileInfo.PolicyRule implements java.lang.annotation.Annotation { + } + + public static abstract class EuiccProfileInfo.ProfileClass implements java.lang.annotation.Annotation { + } + + public static abstract class EuiccProfileInfo.ProfileState implements java.lang.annotation.Annotation { + } + + public abstract class EuiccService extends android.app.Service { + ctor public EuiccService(); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract int onDeleteSubscription(int, java.lang.String); + method public abstract int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean); + method public abstract int onEraseSubscriptions(int); + method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean); + method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean); + method public abstract java.lang.String onGetEid(int); + method public abstract android.telephony.euicc.EuiccInfo onGetEuiccInfo(int); + method public abstract android.service.euicc.GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int); + method public abstract int onGetOtaStatus(int); + method public abstract int onRetainSubscriptionsForFactoryReset(int); + method public abstract void onStartOtaIfNecessary(int, android.service.euicc.EuiccService.OtaStatusChangedCallback); + method public abstract int onSwitchToSubscription(int, java.lang.String, boolean); + method public abstract int onUpdateSubscriptionNickname(int, java.lang.String, java.lang.String); + field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS"; + field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; + field public static final java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; + field public static final java.lang.String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM"; + field public static final java.lang.String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES"; + field public static final java.lang.String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI"; + field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService"; + field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; + field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE"; + field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; + field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT"; + field public static final int RESULT_FIRST_USER = 1; // 0x1 + field public static final int RESULT_MUST_DEACTIVATE_SIM = -1; // 0xffffffff + field public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe + field public static final int RESULT_OK = 0; // 0x0 + } + + public static abstract class EuiccService.OtaStatusChangedCallback { + ctor public EuiccService.OtaStatusChangedCallback(); + method public abstract void onOtaStatusChanged(int); + } + + public final class GetDefaultDownloadableSubscriptionListResult implements android.os.Parcelable { + ctor public GetDefaultDownloadableSubscriptionListResult(int, android.telephony.euicc.DownloadableSubscription[]); + method public int describeContents(); + method public java.util.List getDownloadableSubscriptions(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class GetDownloadableSubscriptionMetadataResult implements android.os.Parcelable { + ctor public GetDownloadableSubscriptionMetadataResult(int, android.telephony.euicc.DownloadableSubscription); + method public int describeContents(); + method public android.telephony.euicc.DownloadableSubscription getDownloadableSubscription(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class GetEuiccProfileInfoListResult implements android.os.Parcelable { + ctor public GetEuiccProfileInfoListResult(int, android.service.euicc.EuiccProfileInfo[], boolean); + method public int describeContents(); + method public boolean getIsRemovable(); + method public java.util.List getProfiles(); + method public int getResult(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + +} + package android.service.notification { public final class Adjustment implements android.os.Parcelable { @@ -4897,8 +5073,14 @@ package android.telephony { field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf } + public class SubscriptionInfo implements android.os.Parcelable { + method public java.util.List getAccessRules(); + } + public class SubscriptionManager { + method public java.util.List getAvailableSubscriptionInfoList(); method public java.util.List getSubscriptionPlans(int); + method public void requestEmbeddedSubscriptionInfoListRefresh(); method public void setSubscriptionOverrideCongested(int, boolean, long); method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionPlans(int, java.util.List); @@ -5034,23 +5216,31 @@ package android.telephony { field public static final int SIM_STATE_PRESENT = 11; // 0xb } + public final class UiccAccessRule implements android.os.Parcelable { + ctor public UiccAccessRule(byte[], java.lang.String, long); + method public int describeContents(); + method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo); + method public int getCarrierPrivilegeStatus(android.content.pm.Signature, java.lang.String); + method public java.lang.String getCertificateHexString(); + method public java.lang.String getPackageName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public class UiccSlotInfo implements android.os.Parcelable { - ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int); + ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int); method public int describeContents(); method public java.lang.String getCardId(); method public int getCardStateInfo(); method public boolean getIsActive(); method public boolean getIsEuicc(); + method public int getLogicalSlotIdx(); method public void writeToParcel(android.os.Parcel, int); field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1 field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3 field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2 field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4 field public static final android.os.Parcelable.Creator CREATOR; - field public final java.lang.String cardId; - field public final int cardStateInfo; - field public final boolean isActive; - field public final boolean isEuicc; } public abstract class VisualVoicemailService extends android.app.Service { @@ -5147,6 +5337,125 @@ package android.telephony.data { } +package android.telephony.euicc { + + public final class DownloadableSubscription implements android.os.Parcelable { + method public java.util.List getAccessRules(); + method public java.lang.String getCarrierName(); + } + + public static final class DownloadableSubscription.Builder { + ctor public DownloadableSubscription.Builder(); + ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription); + method public android.telephony.euicc.DownloadableSubscription build(); + method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List); + method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(java.lang.String); + method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(java.lang.String); + method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(java.lang.String); + } + + public class EuiccCardManager { + method public void authenticateServer(java.lang.String, java.lang.String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void cancelSession(java.lang.String, byte[], int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void deleteProfile(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void disableProfile(java.lang.String, java.lang.String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void listNotifications(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void loadBoundProfilePackage(java.lang.String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void prepareDownload(java.lang.String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void removeNotificationFromList(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestAllProfiles(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestDefaultSmdpAddress(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestEuiccChallenge(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestEuiccInfo1(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestEuiccInfo2(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestProfile(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestRulesAuthTable(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void requestSmdsAddress(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void resetMemory(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void retrieveNotification(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void retrieveNotificationList(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void setDefaultSmdpAddress(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void setNickname(java.lang.String, java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + method public void switchToProfile(java.lang.String, java.lang.String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback); + field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0 + field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1 + field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3 + field public static final int CANCEL_REASON_TIMEOUT = 2; // 0x2 + field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2 + field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1 + field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff + } + + public static abstract class EuiccCardManager.CancelReason implements java.lang.annotation.Annotation { + } + + public static abstract class EuiccCardManager.ResetOption implements java.lang.annotation.Annotation { + } + + public static abstract interface EuiccCardManager.ResultCallback { + method public abstract void onComplete(int, T); + } + + public class EuiccManager { + method public void continueOperation(android.content.Intent, android.os.Bundle); + method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent); + method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent); + method public int getOtaStatus(); + field public static final java.lang.String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION"; + field public static final int EUICC_OTA_FAILED = 2; // 0x2 + field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1 + field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4 + field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5 + field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3 + field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION"; + field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS"; + } + + public static abstract class EuiccManager.OtaStatus implements java.lang.annotation.Annotation { + } + + public final class EuiccNotification implements android.os.Parcelable { + ctor public EuiccNotification(int, java.lang.String, int, byte[]); + method public int describeContents(); + method public byte[] getData(); + method public int getEvent(); + method public int getSeq(); + method public java.lang.String getTargetAddr(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int ALL_EVENTS = 15; // 0xf + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int EVENT_DELETE = 8; // 0x8 + field public static final int EVENT_DISABLE = 4; // 0x4 + field public static final int EVENT_ENABLE = 2; // 0x2 + field public static final int EVENT_INSTALL = 1; // 0x1 + } + + public static abstract class EuiccNotification.Event implements java.lang.annotation.Annotation { + } + + public final class EuiccRulesAuthTable implements android.os.Parcelable { + method public int describeContents(); + method public int findIndex(int, android.service.carrier.CarrierIdentifier); + method public boolean hasPolicyRuleFlag(int, int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; // 0x1 + } + + public static final class EuiccRulesAuthTable.Builder { + ctor public EuiccRulesAuthTable.Builder(int); + method public android.telephony.euicc.EuiccRulesAuthTable.Builder add(int, java.util.List, int); + method public android.telephony.euicc.EuiccRulesAuthTable build(); + } + + public static abstract class EuiccRulesAuthTable.PolicyRuleFlag implements java.lang.annotation.Annotation { + } + +} + package android.telephony.ims { public final class ImsCallForwardInfo implements android.os.Parcelable { @@ -5711,6 +6020,7 @@ package android.telephony.ims.stub { ctor public ImsCallSessionImplBase(); method public void accept(int, android.telephony.ims.ImsStreamMediaProfile); method public void close(); + method public void deflect(java.lang.String); method public void extendToConference(java.lang.String[]); method public java.lang.String getCallId(); method public android.telephony.ims.ImsCallProfile getCallProfile(); @@ -5820,7 +6130,9 @@ package android.telephony.ims.stub { method public final void onSmsReceived(int, java.lang.String, byte[]) throws java.lang.RuntimeException; method public final void onSmsStatusReportReceived(int, int, java.lang.String, byte[]) throws java.lang.RuntimeException; method public void sendSms(int, int, java.lang.String, java.lang.String, boolean, byte[]); - field public static final int DELIVER_STATUS_ERROR = 2; // 0x2 + field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2 + field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3 + field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4 field public static final int DELIVER_STATUS_OK = 1; // 0x1 field public static final int SEND_STATUS_ERROR = 2; // 0x2 field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4 @@ -5857,12 +6169,7 @@ package android.telephony.ims.stub { package android.telephony.mbms { - public final class DownloadRequest implements android.os.Parcelable { - method public byte[] getOpaqueData(); - } - public static class DownloadRequest.Builder { - method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]); method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String); } diff --git a/api/system-removed.txt b/api/system-removed.txt index 48f43e0880daea0e55f600bbdf16726967b50e5d..58652a297bd8c72f0d27b1d36ca16264ca0ef368 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -91,6 +91,34 @@ package android.os { } +package android.security.keystore.recovery { + + public final class KeyChainSnapshot implements android.os.Parcelable { + method public deprecated byte[] getTrustedHardwarePublicKey(); + } + + public class RecoveryController { + method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; + method public deprecated java.util.List getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; + method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException; + method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException; + method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException; + } + + public class RecoverySession implements java.lang.AutoCloseable { + method public deprecated byte[] start(byte[], byte[], byte[], java.util.List) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException; + } + + public final class WrappedApplicationKey implements android.os.Parcelable { + method public deprecated byte[] getAccount(); + } + + public static class WrappedApplicationKey.Builder { + method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]); + } + +} + package android.service.notification { public abstract class NotificationListenerService extends android.app.Service { diff --git a/api/test-current.txt b/api/test-current.txt index c30c0566b50d8677bd6dda93b5818d4ca2356aa6..d5b43115c1259f85e7589ecac90cc34082e3a7f2 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -167,6 +167,17 @@ package android.app.admin { } +package android.app.backup { + + public class BackupManager { + method public android.content.Intent getConfigurationIntent(java.lang.String); + method public android.content.Intent getDataManagementIntent(java.lang.String); + method public java.lang.String getDataManagementLabel(java.lang.String); + method public java.lang.String getDestinationString(java.lang.String); + } + +} + package android.app.usage { public class StorageStatsManager { @@ -185,6 +196,7 @@ package android.content { public abstract class Context { method public android.os.UserHandle getUser(); method public int getUserId(); + method public void setAutofillCompatibilityEnabled(boolean); } } @@ -215,6 +227,7 @@ package android.content.pm { } public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000 } @@ -532,6 +545,7 @@ package android.provider { } public static final class Settings.Global extends android.provider.Settings.NameValueTable { + field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages"; field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch"; field public static final java.lang.String LOW_POWER_MODE = "low_power"; field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; @@ -541,6 +555,7 @@ package android.provider { field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service"; + field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count"; field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size"; field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size"; field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; @@ -717,6 +732,10 @@ package android.telephony { field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override"; } + public class ServiceState implements android.os.Parcelable { + method public void setSystemAndNetworkId(int, int); + } + } package android.telephony.mbms { @@ -1038,6 +1057,7 @@ package android.view.accessibility { public class AccessibilityNodeInfo implements android.os.Parcelable { method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger); + method public void writeToParcelNoRecycle(android.os.Parcel, int); } public final class AccessibilityWindowInfo implements android.os.Parcelable { @@ -1046,10 +1066,20 @@ package android.view.accessibility { } +package android.view.animation { + + public class AnimationUtils { + method public static void lockAnimationClock(long); + method public static void unlockAnimationClock(); + } + +} + package android.view.autofill { public final class AutofillId implements android.os.Parcelable { ctor public AutofillId(int); + ctor public AutofillId(android.view.autofill.AutofillId, int); } } @@ -1078,6 +1108,17 @@ package android.widget { field public static final int MODE_SPINNER = 1; // 0x1 } + public final class Magnifier { + method public android.graphics.Bitmap getContent(); + method public static android.graphics.PointF getMagnifierDefaultSize(); + method public android.graphics.Rect getWindowPositionOnScreen(); + method public void setOnOperationCompleteCallback(android.widget.Magnifier.Callback); + } + + public static abstract interface Magnifier.Callback { + method public abstract void onOperationComplete(); + } + public class NumberPicker extends android.widget.LinearLayout { method public java.lang.CharSequence getDisplayedValueForCurrentSelection(); } diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 813335a688abe9a5b1ac89477d7aaa4e4f707e38..c04e61b77274ca7b951151d8fb56607a1ce02fe4 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -172,6 +172,8 @@ public class Am extends BaseCommand { } else if (opt.equals("--no_window_animation") || opt.equals("--no-window-animation")) { instrument.noWindowAnimation = true; + } else if (opt.equals("--no-hidden-api-checks")) { + instrument.disableHiddenApiChecks = true; } else if (opt.equals("--user")) { instrument.userId = parseUserArg(nextArgRequired()); } else if (opt.equals("--abi")) { diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java index d79b1a61eb5bf90efadd59cfeb9f1989cad490f8..0dade0b2ba813c8699df33c9a3325f6024f6c877 100644 --- a/cmds/am/src/com/android/commands/am/Instrument.java +++ b/cmds/am/src/com/android/commands/am/Instrument.java @@ -73,12 +73,17 @@ public class Instrument { boolean protoFile = false; // write proto to a file String logPath = null; public boolean noWindowAnimation = false; + public boolean disableHiddenApiChecks = false; public String abi = null; public int userId = UserHandle.USER_CURRENT; public Bundle args = new Bundle(); // Required public String componentNameArg; + // Disable hidden API checks for the newly started instrumentation. + // Must be kept in sync with ActivityManagerService. + private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0; + /** * Construct the instrument command runner. */ @@ -475,7 +480,8 @@ public class Instrument { } // Start the instrumentation - if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, + int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0; + if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId, abi)) { throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); } diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk index 72fe0516f4808010e2a4ad6bac04a6e839b06aee..6acf655c972a3b4fbb73a2626e31c5bfffe4a51a 100644 --- a/cmds/app_process/Android.mk +++ b/cmds/app_process/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) app_process_common_shared_libs := \ libandroid_runtime \ + libbase \ libbinder \ libcutils \ libdl \ diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 1671337511b7d39a2b4a275eda182299382618c9..9ac05dcd402cb01b8fb5541e390afe6cd8f23758 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -20,8 +20,11 @@ #include #include #include +#include #include // for AID_SYSTEM +using android::base::GetProperty; + namespace android { static void app_usage() @@ -186,6 +189,13 @@ static const char ZYGOTE_NICE_NAME[] = "zygote"; int main(int argc, char* const argv[]) { + std::string bootmode = GetProperty("ro.bootmode", ""); + + if ((strncmp(bootmode.c_str(), "ffbm-00", 7) == 0) + || (strncmp(bootmode.c_str(), "ffbm-01", 7) == 0)) { + return 0; + } + if (!LOG_NDEBUG) { String8 argv_String; for (int i = 0; i < argc; ++i) { diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp old mode 100644 new mode 100755 index d69dd79555a1d1f222aed9631cce31566b4c3138..0926b5899468cebbac52d6e2f17e23bafd74a0ef --- a/cmds/idmap/scan.cpp +++ b/cmds/idmap/scan.cpp @@ -233,9 +233,6 @@ namespace { int idmap_scan(const char *target_package_name, const char *target_apk_path, const char *idmap_dir, const android::Vector *overlay_dirs) { - String8 filename = String8(idmap_dir); - filename.appendPath("overlays.list"); - SortedVector overlayVector; const size_t N = overlay_dirs->size(); for (size_t i = 0; i < N; ++i) { @@ -250,39 +247,58 @@ int idmap_scan(const char *target_package_name, const char *target_apk_path, struct stat st; char overlay_apk_path[PATH_MAX + 1]; snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name); + if (stat(overlay_apk_path, &st) < 0) { continue; } - if (!S_ISREG(st.st_mode)) { - continue; - } - int priority = parse_apk(overlay_apk_path, target_package_name); - if (priority < 0) { + if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { continue; } - String8 idmap_path(idmap_dir); - idmap_path.appendPath(flatten_path(overlay_apk_path + 1)); - idmap_path.append("@idmap"); + if (S_ISDIR(st.st_mode)) { + String8 dir_name = String8(overlay_apk_path).getPathLeaf(); + if (dir_name == "." || dir_name == "..") { + // Skip the "." and ".." dir. + continue; + } + android::Vector ovector; + ovector.push(overlay_apk_path); + idmap_scan(target_package_name, target_apk_path, idmap_dir, &ovector); + } else { + int priority = parse_apk(overlay_apk_path, target_package_name); + if (priority < 0) { + continue; + } - if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) { - ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n", - target_apk_path, overlay_apk_path, idmap_path.string()); - continue; - } + String8 filename = String8(idmap_dir); + filename.appendPath("overlays.list"); + if (unlink(filename.string()) != 0 && errno != ENOENT) { + return EXIT_FAILURE; + } + + String8 idmap_path(idmap_dir); + idmap_path.appendPath(flatten_path(overlay_apk_path + 1)); + idmap_path.append("@idmap"); + if (idmap_create_path(target_apk_path, overlay_apk_path, + idmap_path.string()) != 0) { + ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n", + target_apk_path, overlay_apk_path, idmap_path.string()); + continue; + } + + Overlay overlay(String8(overlay_apk_path), idmap_path, priority); + overlayVector.add(overlay); - Overlay overlay(String8(overlay_apk_path), idmap_path, priority); - overlayVector.add(overlay); + if (!writePackagesList(filename.string(), overlayVector)) { + return EXIT_FAILURE; + } + } } closedir(dir); } - if (!writePackagesList(filename.string(), overlayVector)) { - return EXIT_FAILURE; - } - return EXIT_SUCCESS; } diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..1a68a32c43085796caebbe8f0bbf697576cc1d4f --- /dev/null +++ b/cmds/incident_helper/OWNERS @@ -0,0 +1,2 @@ +jinyithu@google.com +kwekua@google.com diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp index 02f1ce7cc0fc39a7898dbc1f22a88f7587da0a53..fde17bd97a7f72a475f8fa142fc1cdee064ad554 100644 --- a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp @@ -62,15 +62,15 @@ CpuFreqParser::Parse(const int in, const int out) const ProtoOutputStream proto; long jiffyHz = sysconf(_SC_CLK_TCK); - proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz); + proto.write(CpuFreqProto::JIFFY_HZ, (int)jiffyHz); for (int i=0; i>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) { - long long stateToken = proto.start(CpuFreqStats::TIMES); - proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first); - proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second); + long long stateToken = proto.start(CpuFreqProto::Stats::TIMES); + proto.write(CpuFreqProto::Stats::TimeInState::STATE_KHZ, it->first); + proto.write(CpuFreqProto::Stats::TimeInState::TIME_JIFFY, it->second); proto.end(stateToken); } proto.end(token); diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp index d73de54d8c5dfa8db19309ce43f92a2d9098a781..b2b431c628661a43939ee3d885099563772a3ca9 100644 --- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp @@ -54,11 +54,11 @@ CpuInfoParser::Parse(const int in, const int out) const bool nextToUsage = false; ProtoOutputStream proto; - Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT); - table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES, - CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT); - table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES, - CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT); + Table table(CpuInfoProto::Task::_FIELD_NAMES, CpuInfoProto::Task::_FIELD_IDS, CpuInfoProto::Task::_FIELD_COUNT); + table.addEnumTypeMap("s", CpuInfoProto::Task::_ENUM_STATUS_NAMES, + CpuInfoProto::Task::_ENUM_STATUS_VALUES, CpuInfoProto::Task::_ENUM_STATUS_COUNT); + table.addEnumTypeMap("pcy", CpuInfoProto::Task::_ENUM_POLICY_NAMES, + CpuInfoProto::Task::_ENUM_POLICY_VALUES, CpuInfoProto::Task::_ENUM_POLICY_COUNT); // parse line by line while (reader.readLine(&line)) { @@ -67,33 +67,33 @@ CpuInfoParser::Parse(const int in, const int out) const nline++; if (stripPrefix(&line, "Tasks:")) { - writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER, - CpuInfo::TaskStats::_FIELD_COUNT, - CpuInfo::TaskStats::_FIELD_NAMES, - CpuInfo::TaskStats::_FIELD_IDS); + writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER, + CpuInfoProto::TaskStats::_FIELD_COUNT, + CpuInfoProto::TaskStats::_FIELD_NAMES, + CpuInfoProto::TaskStats::_FIELD_IDS); continue; } if (stripPrefix(&line, "Mem:")) { - writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER, - CpuInfo::MemStats::_FIELD_COUNT, - CpuInfo::MemStats::_FIELD_NAMES, - CpuInfo::MemStats::_FIELD_IDS); + writeSuffixLine(&proto, CpuInfoProto::MEM, line, COMMA_DELIMITER, + CpuInfoProto::MemStats::_FIELD_COUNT, + CpuInfoProto::MemStats::_FIELD_NAMES, + CpuInfoProto::MemStats::_FIELD_IDS); continue; } if (stripPrefix(&line, "Swap:")) { - writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER, - CpuInfo::MemStats::_FIELD_COUNT, - CpuInfo::MemStats::_FIELD_NAMES, - CpuInfo::MemStats::_FIELD_IDS); + writeSuffixLine(&proto, CpuInfoProto::SWAP, line, COMMA_DELIMITER, + CpuInfoProto::MemStats::_FIELD_COUNT, + CpuInfoProto::MemStats::_FIELD_NAMES, + CpuInfoProto::MemStats::_FIELD_IDS); nextToSwap = true; continue; } if (nextToSwap) { - writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE, - CpuInfo::CpuUsage::_FIELD_COUNT, - CpuInfo::CpuUsage::_FIELD_NAMES, - CpuInfo::CpuUsage::_FIELD_IDS); + writeSuffixLine(&proto, CpuInfoProto::CPU_USAGE, line, DEFAULT_WHITESPACE, + CpuInfoProto::CpuUsage::_FIELD_COUNT, + CpuInfoProto::CpuUsage::_FIELD_NAMES, + CpuInfoProto::CpuUsage::_FIELD_IDS); nextToUsage = true; nextToSwap = false; continue; @@ -138,7 +138,7 @@ CpuInfoParser::Parse(const int in, const int out) const continue; } - long long token = proto.start(CpuInfo::TASKS); + long long token = proto.start(CpuInfoProto::TASKS); for (int i=0; i<(int)record.size(); i++) { if (!table.insertField(&proto, header[i], record[i])) { fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n", diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp index cae51abbe57f18957de0620779cd27ecab210704..28816ea75a75b03c89bc97e283698479a234b2a8 100644 --- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp @@ -33,7 +33,9 @@ KernelWakesParser::Parse(const int in, const int out) const int nline = 0; ProtoOutputStream proto; - Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT); + Table table(KernelWakeSourcesProto::WakeupSource::_FIELD_NAMES, + KernelWakeSourcesProto::WakeupSource::_FIELD_IDS, + KernelWakeSourcesProto::WakeupSource::_FIELD_COUNT); // parse line by line while (reader.readLine(&line)) { @@ -57,7 +59,7 @@ KernelWakesParser::Parse(const int in, const int out) const continue; } - long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES); + long long token = proto.start(KernelWakeSourcesProto::WAKEUP_SOURCES); for (int i=0; i<(int)record.size(); i++) { if (!table.insertField(&proto, header[i], record[i])) { fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp index f1b93ff9ec41cb751bea6175c3f1e97028a0070c..ab4382ac13f40adf4b284d40f3ff341366ca8794 100644 --- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp @@ -33,7 +33,9 @@ PageTypeInfoParser::Parse(const int in, const int out) const header_t blockHeader; ProtoOutputStream proto; - Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT); + Table table(PageTypeInfoProto::Block::_FIELD_NAMES, + PageTypeInfoProto::Block::_FIELD_IDS, + PageTypeInfoProto::Block::_FIELD_COUNT); while (reader.readLine(&line)) { if (line.empty()) { @@ -44,11 +46,11 @@ PageTypeInfoParser::Parse(const int in, const int out) const if (stripPrefix(&line, "Page block order:")) { pageBlockOrder = toInt(line); - proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder); + proto.write(PageTypeInfoProto::PAGE_BLOCK_ORDER, pageBlockOrder); continue; } if (stripPrefix(&line, "Pages per block:")) { - proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line)); + proto.write(PageTypeInfoProto::PAGES_PER_BLOCK, toInt(line)); continue; } if (stripPrefix(&line, "Free pages count per migrate type at order")) { @@ -62,14 +64,14 @@ PageTypeInfoParser::Parse(const int in, const int out) const record_t record = parseRecord(line, COMMA_DELIMITER); if (migrateTypeSession && record.size() == 3) { - long long token = proto.start(PageTypeInfo::MIGRATE_TYPES); + long long token = proto.start(PageTypeInfoProto::MIGRATE_TYPES); // expect part 0 starts with "Node" if (stripPrefix(&record[0], "Node")) { - proto.write(MigrateTypeProto::NODE, toInt(record[0])); + proto.write(PageTypeInfoProto::MigrateType::NODE, toInt(record[0])); } else return BAD_VALUE; // expect part 1 starts with "zone" if (stripPrefix(&record[1], "zone")) { - proto.write(MigrateTypeProto::ZONE, record[1]); + proto.write(PageTypeInfoProto::MigrateType::ZONE, record[1]); } else return BAD_VALUE; // expect part 2 starts with "type" if (stripPrefix(&record[2], "type")) { @@ -83,26 +85,27 @@ PageTypeInfoParser::Parse(const int in, const int out) const int pageCountsSize = pageBlockOrder + 2; if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE; - proto.write(MigrateTypeProto::TYPE, pageCounts[0]); + proto.write(PageTypeInfoProto::MigrateType::TYPE, pageCounts[0]); for (auto i=1; i0; i--) { table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str()); } proto.end(token); } if (!zram.empty()) { - long long token = proto.start(SummaryProto::ZRAM); - proto.write(ZramProto::RAW_TEXT, zram); + long long token = proto.start(ProcrankProto::Summary::ZRAM); + proto.write(ProcrankProto::Summary::Zram::RAW_TEXT, zram); proto.end(token); } if (!ram.empty()) { - long long token = proto.start(SummaryProto::RAM); - proto.write(RamProto::RAW_TEXT, ram); + long long token = proto.start(ProcrankProto::Summary::RAM); + proto.write(ProcrankProto::Summary::Ram::RAW_TEXT, ram); proto.end(token); } proto.end(token); diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp index e9014cacfa0b2dda1151c0111660cc38020b845f..420775fb5f04b66067d656504afaaf1c520adbbb 100644 --- a/cmds/incident_helper/src/parsers/PsParser.cpp +++ b/cmds/incident_helper/src/parsers/PsParser.cpp @@ -33,12 +33,12 @@ status_t PsParser::Parse(const int in, const int out) const { int diff = 0; ProtoOutputStream proto; - Table table(PsDumpProto::Process::_FIELD_NAMES, PsDumpProto::Process::_FIELD_IDS, PsDumpProto::Process::_FIELD_COUNT); + Table table(PsProto::Process::_FIELD_NAMES, PsProto::Process::_FIELD_IDS, PsProto::Process::_FIELD_COUNT); const char* pcyNames[] = { "fg", "bg", "ta" }; - const int pcyValues[] = {PsDumpProto::Process::POLICY_FG, PsDumpProto::Process::POLICY_BG, PsDumpProto::Process::POLICY_TA}; + const int pcyValues[] = {PsProto::Process::POLICY_FG, PsProto::Process::POLICY_BG, PsProto::Process::POLICY_TA}; table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3); const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" }; - const int sValues[] = {PsDumpProto::Process::STATE_D, PsDumpProto::Process::STATE_R, PsDumpProto::Process::STATE_S, PsDumpProto::Process::STATE_T, PsDumpProto::Process::STATE_TRACING, PsDumpProto::Process::STATE_X, PsDumpProto::Process::STATE_Z}; + const int sValues[] = {PsProto::Process::STATE_D, PsProto::Process::STATE_R, PsProto::Process::STATE_S, PsProto::Process::STATE_T, PsProto::Process::STATE_TRACING, PsProto::Process::STATE_X, PsProto::Process::STATE_Z}; table.addEnumTypeMap("s", sNames, sValues, 7); // Parse line by line @@ -71,7 +71,7 @@ status_t PsParser::Parse(const int in, const int out) const { continue; } - long long token = proto.start(PsDumpProto::PROCESSES); + long long token = proto.start(PsProto::PROCESSES); for (int i=0; i<(int)record.size(); i++) { if (!table.insertField(&proto, header[i], record[i])) { fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp index 82deee42687fdce3a10b9770ca7cc798fb07d467..0839a7e45eddb14fd192266d14d72126690234e0 100644 --- a/cmds/incident_helper/tests/CpuFreqParser_test.cpp +++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp @@ -52,14 +52,14 @@ protected: TEST_F(CpuFreqParserTest, Success) { const string testFile = kTestDataPath + "cpufreq.txt"; CpuFreqParser parser; - CpuFreq expected; + CpuFreqProto expected; long jiffyHz = sysconf(_SC_CLK_TCK); expected.set_jiffy_hz(jiffyHz); - CpuFreqStats::TimeInState* state; + CpuFreqProto::Stats::TimeInState* state; - CpuFreqStats* cpu0 = expected.add_cpu_freqs(); + CpuFreqProto::Stats* cpu0 = expected.add_cpu_freqs(); cpu0->set_cpu_name("cpu0"); state = cpu0->add_times(); state->set_state_khz(307200); @@ -71,7 +71,7 @@ TEST_F(CpuFreqParserTest, Success) { state->set_state_khz(768000); state->set_time_jiffy(22652); - CpuFreqStats* cpu1 = expected.add_cpu_freqs(); + CpuFreqProto::Stats* cpu1 = expected.add_cpu_freqs(); cpu1->set_cpu_name("cpu1"); state = cpu1->add_times(); state->set_state_khz(307200); @@ -83,7 +83,7 @@ TEST_F(CpuFreqParserTest, Success) { state->set_state_khz(768000); state->set_time_jiffy(22652); - CpuFreqStats* cpu2 = expected.add_cpu_freqs(); + CpuFreqProto::Stats* cpu2 = expected.add_cpu_freqs(); cpu2->set_cpu_name("cpu2"); state = cpu2->add_times(); state->set_state_khz(307200); @@ -98,7 +98,7 @@ TEST_F(CpuFreqParserTest, Success) { state->set_state_khz(825600); state->set_time_jiffy(13173); - CpuFreqStats* cpu3 = expected.add_cpu_freqs(); + CpuFreqProto::Stats* cpu3 = expected.add_cpu_freqs(); cpu3->set_cpu_name("cpu3"); state = cpu3->add_times(); state->set_state_khz(307200); diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp index 8dce53e4649283cf044b399211ccb75e5b10f995..2e50d631715eaf83f1ecb55b8d8529b47935336b 100644 --- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp +++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp @@ -52,28 +52,28 @@ protected: TEST_F(CpuInfoParserTest, Success) { const string testFile = kTestDataPath + "cpuinfo.txt"; CpuInfoParser parser; - CpuInfo expected; + CpuInfoProto expected; - CpuInfo::TaskStats* taskStats = expected.mutable_task_stats(); + CpuInfoProto::TaskStats* taskStats = expected.mutable_task_stats(); taskStats->set_total(2038); taskStats->set_running(1); taskStats->set_sleeping(2033); taskStats->set_stopped(0); taskStats->set_zombie(0); - CpuInfo::MemStats* mem = expected.mutable_mem(); + CpuInfoProto::MemStats* mem = expected.mutable_mem(); mem->set_total(3842668); mem->set_used(3761936); mem->set_free(80732); mem->set_buffers(220188); - CpuInfo::MemStats* swap = expected.mutable_swap(); + CpuInfoProto::MemStats* swap = expected.mutable_swap(); swap->set_total(524284); swap->set_used(25892); swap->set_free(498392); swap->set_cached(1316952); - CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage(); + CpuInfoProto::CpuUsage* usage = expected.mutable_cpu_usage(); usage->set_cpu(400); usage->set_user(17); usage->set_nice(0); @@ -85,59 +85,59 @@ TEST_F(CpuInfoParserTest, Success) { usage->set_host(0); // This is a special line which is able to be parsed by the CpuInfoParser - CpuInfo::Task* task1 = expected.add_tasks(); + CpuInfoProto::Task* task1 = expected.add_tasks(); task1->set_pid(29438); task1->set_tid(29438); task1->set_user("rootabcdefghij"); task1->set_pr("20"); task1->set_ni(0); task1->set_cpu(57.9); - task1->set_s(CpuInfo::Task::STATUS_R); + task1->set_s(CpuInfoProto::Task::STATUS_R); task1->set_virt("14M"); task1->set_res("3.8M"); - task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN); + task1->set_pcy(CpuInfoProto::Task::POLICY_UNKNOWN); task1->set_cmd("top test"); task1->set_name("top"); - CpuInfo::Task* task2 = expected.add_tasks(); + CpuInfoProto::Task* task2 = expected.add_tasks(); task2->set_pid(916); task2->set_tid(916); task2->set_user("system"); task2->set_pr("18"); task2->set_ni(-2); task2->set_cpu(1.4); - task2->set_s(CpuInfo::Task::STATUS_S); + task2->set_s(CpuInfoProto::Task::STATUS_S); task2->set_virt("4.6G"); task2->set_res("404M"); - task2->set_pcy(CpuInfo::Task::POLICY_fg); + task2->set_pcy(CpuInfoProto::Task::POLICY_fg); task2->set_cmd("system_server"); task2->set_name("system_server"); - CpuInfo::Task* task3 = expected.add_tasks(); + CpuInfoProto::Task* task3 = expected.add_tasks(); task3->set_pid(28); task3->set_tid(28); task3->set_user("root"); task3->set_pr("-2"); task3->set_ni(0); task3->set_cpu(1.4); - task3->set_s(CpuInfo::Task::STATUS_S); + task3->set_s(CpuInfoProto::Task::STATUS_S); task3->set_virt("0"); task3->set_res("0"); - task3->set_pcy(CpuInfo::Task::POLICY_bg); + task3->set_pcy(CpuInfoProto::Task::POLICY_bg); task3->set_cmd("rcuc/3"); task3->set_name("[rcuc/3]"); - CpuInfo::Task* task4 = expected.add_tasks(); + CpuInfoProto::Task* task4 = expected.add_tasks(); task4->set_pid(27); task4->set_tid(27); task4->set_user("root"); task4->set_pr("RT"); task4->set_ni(0); task4->set_cpu(1.4); - task4->set_s(CpuInfo::Task::STATUS_S); + task4->set_s(CpuInfoProto::Task::STATUS_S); task4->set_virt("0"); task4->set_res("0"); - task4->set_pcy(CpuInfo::Task::POLICY_ta); + task4->set_pcy(CpuInfoProto::Task::POLICY_ta); task4->set_cmd("migration/3"); task4->set_name("[migration/3]"); diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp index a98c62bd6024225f280f26648cf8a11b857f4a64..f92d81361eaba00845dbcb14a6b22b6d0dba0f8b 100644 --- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp +++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp @@ -52,14 +52,14 @@ protected: TEST_F(KernelWakesParserTest, Short) { const string testFile = kTestDataPath + "kernel_wakeups_short.txt"; KernelWakesParser parser; - KernelWakeSources expected; + KernelWakeSourcesProto expected; - WakeupSourceProto* record1 = expected.add_wakeup_sources(); + KernelWakeSourcesProto::WakeupSource* record1 = expected.add_wakeup_sources(); record1->set_name("ab"); record1->set_active_count(8); record1->set_last_change(123456123456LL); - WakeupSourceProto* record2 = expected.add_wakeup_sources(); + KernelWakeSourcesProto::WakeupSource* record2 = expected.add_wakeup_sources(); record2->set_name("df"); record2->set_active_count(143); record2->set_last_change(0LL); @@ -76,9 +76,9 @@ TEST_F(KernelWakesParserTest, Short) { TEST_F(KernelWakesParserTest, Normal) { const string testFile = kTestDataPath + "kernel_wakeups.txt"; KernelWakesParser parser; - KernelWakeSources expected; + KernelWakeSourcesProto expected; - WakeupSourceProto* record1 = expected.add_wakeup_sources(); + KernelWakeSourcesProto::WakeupSource* record1 = expected.add_wakeup_sources(); record1->set_name("ipc000000ab_ATFWD-daemon"); record1->set_active_count(8); record1->set_event_count(8); @@ -90,7 +90,7 @@ TEST_F(KernelWakesParserTest, Normal) { record1->set_last_change(131348LL); record1->set_prevent_suspend_time(0LL); - WakeupSourceProto* record2 = expected.add_wakeup_sources(); + KernelWakeSourcesProto::WakeupSource* record2 = expected.add_wakeup_sources(); record2->set_name("ipc000000aa_ATFWD-daemon"); record2->set_active_count(143); record2->set_event_count(143); diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp index a9e6e816c6c5efeb25433d2a89bffd3ba088fc3a..9bad7be4a07e8c24b63452d53133780789ae3687 100644 --- a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp +++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp @@ -52,12 +52,12 @@ protected: TEST_F(PageTypeInfoParserTest, Success) { const string testFile = kTestDataPath + "pagetypeinfo.txt"; PageTypeInfoParser parser; - PageTypeInfo expected; + PageTypeInfoProto expected; expected.set_page_block_order(10); expected.set_pages_per_block(1024); - MigrateTypeProto* mt1 = expected.add_migrate_types(); + PageTypeInfoProto::MigrateType* mt1 = expected.add_migrate_types(); mt1->set_node(0); mt1->set_zone("DMA"); mt1->set_type("Unmovable"); @@ -66,7 +66,7 @@ TEST_F(PageTypeInfoParserTest, Success) { mt1->add_free_pages_count(arr1[i]); } - MigrateTypeProto* mt2 = expected.add_migrate_types(); + PageTypeInfoProto::MigrateType* mt2 = expected.add_migrate_types(); mt2->set_node(0); mt2->set_zone("Normal"); mt2->set_type("Reclaimable"); @@ -75,7 +75,7 @@ TEST_F(PageTypeInfoParserTest, Success) { mt2->add_free_pages_count(arr2[i]); } - BlockProto* block1 = expected.add_blocks(); + PageTypeInfoProto::Block* block1 = expected.add_blocks(); block1->set_node(0); block1->set_zone("DMA"); block1->set_unmovable(74); @@ -86,7 +86,7 @@ TEST_F(PageTypeInfoParserTest, Success) { block1->set_isolate(0); - BlockProto* block2 = expected.add_blocks(); + PageTypeInfoProto::Block* block2 = expected.add_blocks(); block2->set_node(0); block2->set_zone("Normal"); block2->set_unmovable(70); diff --git a/cmds/incident_helper/tests/ProcrankParser_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp index 76b25d7f8f4792bfec5be780653d2567bf6a8ab5..0b567aec9597cc2056bf647b4ed572dd6b66df1a 100644 --- a/cmds/incident_helper/tests/ProcrankParser_test.cpp +++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp @@ -52,9 +52,9 @@ protected: TEST_F(ProcrankParserTest, HasSwapInfo) { const string testFile = kTestDataPath + "procrank.txt"; ProcrankParser parser; - Procrank expected; + ProcrankProto expected; - ProcessProto* process1 = expected.add_processes(); + ProcrankProto::Process* process1 = expected.add_processes(); process1->set_pid(1119); process1->set_vss(2607640); process1->set_rss(339564); @@ -66,7 +66,7 @@ TEST_F(ProcrankParserTest, HasSwapInfo) { process1->set_zswap(10); process1->set_cmdline("system_server"); - ProcessProto* process2 = expected.add_processes(); + ProcrankProto::Process* process2 = expected.add_processes(); process2->set_pid(649); process2->set_vss(11016); process2->set_rss(1448); @@ -78,7 +78,7 @@ TEST_F(ProcrankParserTest, HasSwapInfo) { process2->set_zswap(75); process2->set_cmdline("/vendor/bin/qseecomd"); - ProcessProto* total = expected.mutable_summary()->mutable_total(); + ProcrankProto::Process* total = expected.mutable_summary()->mutable_total(); total->set_pss(1201993); total->set_uss(935300); total->set_swap(88164); @@ -104,9 +104,9 @@ TEST_F(ProcrankParserTest, HasSwapInfo) { TEST_F(ProcrankParserTest, NoSwapInfo) { const string testFile = kTestDataPath + "procrank_short.txt"; ProcrankParser parser; - Procrank expected; + ProcrankProto expected; - ProcessProto* process1 = expected.add_processes(); + ProcrankProto::Process* process1 = expected.add_processes(); process1->set_pid(1119); process1->set_vss(2607640); process1->set_rss(339564); @@ -114,7 +114,7 @@ TEST_F(ProcrankParserTest, NoSwapInfo) { process1->set_uss(114216); process1->set_cmdline("system_server"); - ProcessProto* process2 = expected.add_processes(); + ProcrankProto::Process* process2 = expected.add_processes(); process2->set_pid(649); process2->set_vss(11016); process2->set_rss(1448); @@ -122,7 +122,7 @@ TEST_F(ProcrankParserTest, NoSwapInfo) { process2->set_uss(48); process2->set_cmdline("/vendor/bin/qseecomd"); - ProcessProto* total = expected.mutable_summary()->mutable_total(); + ProcrankProto::Process* total = expected.mutable_summary()->mutable_total(); total->set_pss(1201993); total->set_uss(935300); total->set_cmdline("TOTAL"); diff --git a/cmds/incident_helper/tests/PsParser_test.cpp b/cmds/incident_helper/tests/PsParser_test.cpp index 1f03a7f3a332ba69dc07627fb3034bf4e19409d7..114d63472ffc6d0959e38642f9666e0f56a39f98 100644 --- a/cmds/incident_helper/tests/PsParser_test.cpp +++ b/cmds/incident_helper/tests/PsParser_test.cpp @@ -52,10 +52,10 @@ protected: TEST_F(PsParserTest, Normal) { const string testFile = kTestDataPath + "ps.txt"; PsParser parser; - PsDumpProto expected; - PsDumpProto got; + PsProto expected; + PsProto got; - PsDumpProto::Process* record1 = expected.add_processes(); + PsProto::Process* record1 = expected.add_processes(); record1->set_label("u:r:init:s0"); record1->set_user("root"); record1->set_pid(1); @@ -65,16 +65,16 @@ TEST_F(PsParserTest, Normal) { record1->set_rss(2636); record1->set_wchan("SyS_epoll_wait"); record1->set_addr("0"); - record1->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record1->set_s(PsProto_Process_ProcessStateCode_STATE_S); record1->set_pri(19); record1->set_ni(0); record1->set_rtprio("-"); - record1->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record1->set_pcy(PsDumpProto::Process::POLICY_FG); + record1->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record1->set_pcy(PsProto::Process::POLICY_FG); record1->set_time("00:00:01"); record1->set_cmd("init"); - PsDumpProto::Process* record2 = expected.add_processes(); + PsProto::Process* record2 = expected.add_processes(); record2->set_label("u:r:kernel:s0"); record2->set_user("root"); record2->set_pid(2); @@ -84,16 +84,16 @@ TEST_F(PsParserTest, Normal) { record2->set_rss(0); record2->set_wchan("kthreadd"); record2->set_addr("0"); - record2->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record2->set_s(PsProto_Process_ProcessStateCode_STATE_S); record2->set_pri(19); record2->set_ni(0); record2->set_rtprio("-"); - record2->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record2->set_pcy(PsDumpProto::Process::POLICY_FG); + record2->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record2->set_pcy(PsProto::Process::POLICY_FG); record2->set_time("00:00:00"); record2->set_cmd("kthreadd"); - PsDumpProto::Process* record3 = expected.add_processes(); + PsProto::Process* record3 = expected.add_processes(); record3->set_label("u:r:surfaceflinger:s0"); record3->set_user("system"); record3->set_pid(499); @@ -103,16 +103,16 @@ TEST_F(PsParserTest, Normal) { record3->set_rss(22024); record3->set_wchan("futex_wait_queue_me"); record3->set_addr("0"); - record3->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record3->set_s(PsProto_Process_ProcessStateCode_STATE_S); record3->set_pri(42); record3->set_ni(-9); record3->set_rtprio("2"); - record3->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_FIFO); - record3->set_pcy(PsDumpProto::Process::POLICY_FG); + record3->set_sch(PsProto_Process_SchedulingPolicy_SCH_FIFO); + record3->set_pcy(PsProto::Process::POLICY_FG); record3->set_time("00:00:00"); record3->set_cmd("EventThread"); - PsDumpProto::Process* record4 = expected.add_processes(); + PsProto::Process* record4 = expected.add_processes(); record4->set_label("u:r:hal_gnss_default:s0"); record4->set_user("gps"); record4->set_pid(670); @@ -122,16 +122,16 @@ TEST_F(PsParserTest, Normal) { record4->set_rss(7272); record4->set_wchan("poll_schedule_timeout"); record4->set_addr("0"); - record4->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record4->set_s(PsProto_Process_ProcessStateCode_STATE_S); record4->set_pri(19); record4->set_ni(0); record4->set_rtprio("-"); - record4->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record4->set_pcy(PsDumpProto::Process::POLICY_FG); + record4->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record4->set_pcy(PsProto::Process::POLICY_FG); record4->set_time("00:00:00"); record4->set_cmd("Loc_hal_worker"); - PsDumpProto::Process* record5 = expected.add_processes(); + PsProto::Process* record5 = expected.add_processes(); record5->set_label("u:r:platform_app:s0:c512,c768"); record5->set_user("u0_a48"); record5->set_pid(1660); @@ -141,16 +141,16 @@ TEST_F(PsParserTest, Normal) { record5->set_rss(138328); record5->set_wchan("binder_thread_read"); record5->set_addr("0"); - record5->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record5->set_s(PsProto_Process_ProcessStateCode_STATE_S); record5->set_pri(35); record5->set_ni(-16); record5->set_rtprio("-"); - record5->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record5->set_pcy(PsDumpProto::Process::POLICY_TA); + record5->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record5->set_pcy(PsProto::Process::POLICY_TA); record5->set_time("00:00:00"); record5->set_cmd("HwBinder:1660_1"); - PsDumpProto::Process* record6 = expected.add_processes(); + PsProto::Process* record6 = expected.add_processes(); record6->set_label("u:r:perfd:s0"); record6->set_user("root"); record6->set_pid(1939); @@ -160,16 +160,16 @@ TEST_F(PsParserTest, Normal) { record6->set_rss(2088); record6->set_wchan("__skb_recv_datagram"); record6->set_addr("7b9782fd14"); - record6->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record6->set_s(PsProto_Process_ProcessStateCode_STATE_S); record6->set_pri(19); record6->set_ni(0); record6->set_rtprio("-"); - record6->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record6->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN); + record6->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record6->set_pcy(PsProto::Process::POLICY_UNKNOWN); record6->set_time("00:00:00"); record6->set_cmd("perfd"); - PsDumpProto::Process* record7 = expected.add_processes(); + PsProto::Process* record7 = expected.add_processes(); record7->set_label("u:r:perfd:s0"); record7->set_user("root"); record7->set_pid(1939); @@ -179,16 +179,16 @@ TEST_F(PsParserTest, Normal) { record7->set_rss(2088); record7->set_wchan("do_sigtimedwait"); record7->set_addr("7b9782ff6c"); - record7->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S); + record7->set_s(PsProto_Process_ProcessStateCode_STATE_S); record7->set_pri(19); record7->set_ni(0); record7->set_rtprio("-"); - record7->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record7->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN); + record7->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record7->set_pcy(PsProto::Process::POLICY_UNKNOWN); record7->set_time("00:00:00"); record7->set_cmd("POSIX timer 0"); - PsDumpProto::Process* record8 = expected.add_processes(); + PsProto::Process* record8 = expected.add_processes(); record8->set_label("u:r:shell:s0"); record8->set_user("shell"); record8->set_pid(2645); @@ -198,12 +198,12 @@ TEST_F(PsParserTest, Normal) { record8->set_rss(2972); record8->set_wchan("0"); record8->set_addr("7f67a2f8b4"); - record8->set_s(PsDumpProto_Process_ProcessStateCode_STATE_R); + record8->set_s(PsProto_Process_ProcessStateCode_STATE_R); record8->set_pri(19); record8->set_ni(0); record8->set_rtprio("-"); - record8->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL); - record8->set_pcy(PsDumpProto::Process::POLICY_FG); + record8->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL); + record8->set_pcy(PsProto::Process::POLICY_FG); record8->set_time("00:00:00"); record8->set_cmd("ps"); @@ -221,8 +221,8 @@ TEST_F(PsParserTest, Normal) { } else { int n = got.processes_size(); for (int i = 0; i < n; i++) { - PsDumpProto::Process g = got.processes(i); - PsDumpProto::Process e = expected.processes(i); + PsProto::Process g = got.processes(i); + PsProto::Process e = expected.processes(i); if (g.label() != e.label()) { fprintf(stderr, "prcs[%d]: Invalid label. Got %s, want %s\n", i, g.label().c_str(), e.label().c_str()); diff --git a/cmds/incidentd/.clang-format b/cmds/incidentd/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..6fa5b474b7157c147ff77b4e449dbf41fe2ed310 --- /dev/null +++ b/cmds/incidentd/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: Google +AllowShortIfStatementsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +BinPackArguments: true +BinPackParameters: true +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +IndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 +AccessModifierOffset: -4 +IncludeCategories: + - Regex: '^"Log\.h"' + Priority: -1 diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index 2b00d9e6a5962be1c8012865dcd03e62ffd7c162..6bdd9becff37b3a33c4a475dd01ec59ff626d971 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -25,16 +25,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := incidentd -LOCAL_SRC_FILES := \ - src/PrivacyBuffer.cpp \ - src/FdBuffer.cpp \ - src/IncidentService.cpp \ - src/Privacy.cpp \ - src/Reporter.cpp \ - src/Section.cpp \ - src/io_util.cpp \ - src/main.cpp \ - src/report_directory.cpp +LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \ LOCAL_CFLAGS += \ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter @@ -56,6 +47,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ @@ -109,19 +101,15 @@ LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter LOCAL_C_INCLUDES += $(LOCAL_PATH)/src -LOCAL_SRC_FILES := \ +LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \ src/PrivacyBuffer.cpp \ src/FdBuffer.cpp \ src/Privacy.cpp \ src/Reporter.cpp \ src/Section.cpp \ - src/io_util.cpp \ + src/Throttler.cpp \ + src/incidentd_util.cpp \ src/report_directory.cpp \ - tests/section_list.cpp \ - tests/PrivacyBuffer_test.cpp \ - tests/FdBuffer_test.cpp \ - tests/Reporter_test.cpp \ - tests/Section_test.cpp \ LOCAL_STATIC_LIBRARIES := \ libgmock \ diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..1a68a32c43085796caebbe8f0bbf697576cc1d4f --- /dev/null +++ b/cmds/incidentd/OWNERS @@ -0,0 +1,2 @@ +jinyithu@google.com +kwekua@google.com diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md index 71c6deb18aac2b90a6273576e44116717e6a80af..1730a6401254096cb5d374cae1a2104c2c477fe7 100644 --- a/cmds/incidentd/README.md +++ b/cmds/incidentd/README.md @@ -20,4 +20,8 @@ Run the test via AndroidTest.xml ``` root$ atest incidentd_test -``` \ No newline at end of file +``` + +Use clang-format to style the file + +clang-format -style=file -i \ No newline at end of file diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index 0fff4e6dc4a02577ebc0ba82b164a908f458e098..db60794a72255dfd5cd2737060d00d073d094bcd 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "FdBuffer.h" @@ -26,30 +26,16 @@ #include #include -const bool DEBUG = false; -const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB -const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max +const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB +const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max FdBuffer::FdBuffer() - :mBuffer(BUFFER_SIZE), - mStartTime(-1), - mFinishTime(-1), - mTimedOut(false), - mTruncated(false) -{ -} + : mBuffer(BUFFER_SIZE), mStartTime(-1), mFinishTime(-1), mTimedOut(false), mTruncated(false) {} -FdBuffer::~FdBuffer() -{ -} +FdBuffer::~FdBuffer() {} -status_t -FdBuffer::read(int fd, int64_t timeout) -{ - struct pollfd pfds = { - .fd = fd, - .events = POLLIN - }; +status_t FdBuffer::read(int fd, int64_t timeout) { + struct pollfd pfds = {.fd = fd, .events = POLLIN}; mStartTime = uptimeMillis(); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); @@ -63,22 +49,22 @@ FdBuffer::read(int fd, int64_t timeout) int64_t remainingTime = (mStartTime + timeout) - uptimeMillis(); if (remainingTime <= 0) { - if (DEBUG) ALOGD("timed out due to long read"); + VLOG("timed out due to long read"); mTimedOut = true; break; } int count = poll(&pfds, 1, remainingTime); if (count == 0) { - if (DEBUG) ALOGD("timed out due to block calling poll"); + VLOG("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { - if (DEBUG) ALOGD("poll failed: %s", strerror(errno)); + VLOG("poll failed: %s", strerror(errno)); return -errno; } else { if ((pfds.revents & POLLERR) != 0) { - if (DEBUG) ALOGD("return event has error %s", strerror(errno)); + VLOG("return event has error %s", strerror(errno)); return errno != 0 ? -errno : UNKNOWN_ERROR; } else { ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()); @@ -86,7 +72,7 @@ FdBuffer::read(int fd, int64_t timeout) if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; } else { - if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno)); + VLOG("Fail to read %d: %s", fd, strerror(errno)); return -errno; } } else if (amt == 0) { @@ -100,13 +86,12 @@ FdBuffer::read(int fd, int64_t timeout) return NO_ERROR; } -status_t -FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs) -{ +status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, + const bool isSysfs) { struct pollfd pfds[] = { - { .fd = fd, .events = POLLIN }, - { .fd = toFd, .events = POLLOUT }, - { .fd = fromFd, .events = POLLIN }, + {.fd = fd, .events = POLLIN}, + {.fd = toFd, .events = POLLOUT}, + {.fd = fromFd, .events = POLLIN}, }; mStartTime = uptimeMillis(); @@ -131,7 +116,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis(); if (remainingTime <= 0) { - if (DEBUG) ALOGD("timed out due to long read"); + VLOG("timed out due to long read"); mTimedOut = true; break; } @@ -139,11 +124,11 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou // wait for any pfds to be ready to perform IO int count = poll(pfds, 3, remainingTime); if (count == 0) { - if (DEBUG) ALOGD("timed out due to block calling poll"); + VLOG("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { - if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno)); + VLOG("Fail to poll: %s", strerror(errno)); return -errno; } @@ -151,10 +136,10 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou for (int i = 0; i < 3; ++i) { if ((pfds[i].revents & POLLERR) != 0) { if (i == 0 && isSysfs) { - if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd); + VLOG("fd %d is sysfs, ignore its POLLERR return value", fd); continue; } - if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno)); + VLOG("fd[%d]=%d returns error events: %s", i, fd, strerror(errno)); return errno != 0 ? -errno : UNKNOWN_ERROR; } } @@ -169,9 +154,9 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou } if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno)); + VLOG("Fail to read fd %d: %s", fd, strerror(errno)); return -errno; - } // otherwise just continue + } // otherwise just continue } else if (amt == 0) { // reach EOF so don't have to poll pfds[0]. ::close(pfds[0].fd); pfds[0].fd = -1; @@ -191,9 +176,9 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou } if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno)); + VLOG("Fail to write toFd %d: %s", toFd, strerror(errno)); return -errno; - } // otherwise just continue + } // otherwise just continue } else { wpos += amt; cirSize -= amt; @@ -218,9 +203,9 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite()); if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno)); + VLOG("Fail to read fromFd %d: %s", fromFd, strerror(errno)); return -errno; - } // otherwise just continue + } // otherwise just continue } else if (amt == 0) { break; } else { @@ -232,14 +217,6 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou return NO_ERROR; } -size_t -FdBuffer::size() const -{ - return mBuffer.size(); -} +size_t FdBuffer::size() const { return mBuffer.size(); } -EncodedBuffer::iterator -FdBuffer::data() const -{ - return mBuffer.begin(); -} +EncodedBuffer::iterator FdBuffer::data() const { return mBuffer.begin(); } diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h index 48dc855e71b28d6906a2b20cba0ccb6e545efd5d..5bfa0938f5e88fa5ba3e86f06389e62716b77480 100644 --- a/cmds/incidentd/src/FdBuffer.h +++ b/cmds/incidentd/src/FdBuffer.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef FD_BUFFER_H #define FD_BUFFER_H @@ -27,8 +28,7 @@ using namespace std; /** * Reads a file into a buffer, and then writes that data to an FdSet. */ -class FdBuffer -{ +class FdBuffer { public: FdBuffer(); ~FdBuffer(); @@ -50,7 +50,8 @@ public: * * Poll will return POLLERR if fd is from sysfs, handle this edge case. */ - status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false); + status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, + const bool isSysfs = false); /** * Whether we timed out. @@ -90,4 +91,4 @@ private: bool mTruncated; }; -#endif // FD_BUFFER_H +#endif // FD_BUFFER_H diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 1d5ab59f9ba81192697429cb583d97a9693c03d2..28fb38a79a3582683d458b5f84dae5b1fc6d3709 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -13,15 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "IncidentService.h" +#include "FdBuffer.h" +#include "PrivacyBuffer.h" #include "Reporter.h" +#include "incidentd_util.h" +#include "section_list.h" #include +#include #include +#include #include #include #include @@ -29,23 +35,22 @@ #include using namespace android; +using namespace android::base; -enum { - WHAT_RUN_REPORT = 1, - WHAT_SEND_BACKLOG_TO_DROPBOX = 2 -}; +enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 }; -//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5) #define DEFAULT_BACKLOG_DELAY_NS (1000000000LL) +#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB +#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day + // ================================================================================ String16 const DUMP_PERMISSION("android.permission.DUMP"); String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS"); -static Status -checkIncidentPermissions(const IncidentReportArgs& args) -{ +static Status checkIncidentPermissions(const IncidentReportArgs& args) { uid_t callingUid = IPCThreadState::self()->getCallingUid(); + pid_t callingPid = IPCThreadState::self()->getCallingPid(); if (callingUid == AID_ROOT || callingUid == AID_SHELL) { // root doesn't have permission.DUMP if don't do this! return Status::ok(); @@ -54,52 +59,52 @@ checkIncidentPermissions(const IncidentReportArgs& args) // checking calling permission. if (!checkCallingPermission(DUMP_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP", - IPCThreadState::self()->getCallingPid(), callingUid); - return Status::fromExceptionCode(Status::EX_SECURITY, + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, "Calling process does not have permission: android.permission.DUMP"); } if (!checkCallingPermission(USAGE_STATS_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS", - IPCThreadState::self()->getCallingPid(), callingUid); - return Status::fromExceptionCode(Status::EX_SECURITY, + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, "Calling process does not have permission: android.permission.USAGE_STATS"); } // checking calling request uid permission. switch (args.dest()) { case DEST_LOCAL: - if (callingUid != AID_SHELL || callingUid != AID_ROOT) { - return Status::fromExceptionCode(Status::EX_SECURITY, - "Calling process does not have permission to get local data."); + if (callingUid != AID_SHELL && callingUid != AID_ROOT) { + ALOGW("Calling pid %d and uid %d does not have permission to get local data.", + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, + "Calling process does not have permission to get local data."); } case DEST_EXPLICIT: - if (callingUid != AID_SHELL || callingUid != AID_ROOT || - callingUid != AID_STATSD || callingUid != AID_SYSTEM) { - return Status::fromExceptionCode(Status::EX_SECURITY, - "Calling process does not have permission to get explicit data."); + if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD && + callingUid != AID_SYSTEM) { + ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.", + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, + "Calling process does not have permission to get explicit data."); } } return Status::ok(); } // ================================================================================ -ReportRequestQueue::ReportRequestQueue() -{ -} +ReportRequestQueue::ReportRequestQueue() {} -ReportRequestQueue::~ReportRequestQueue() -{ -} +ReportRequestQueue::~ReportRequestQueue() {} -void -ReportRequestQueue::addRequest(const sp& request) -{ +void ReportRequestQueue::addRequest(const sp& request) { unique_lock lock(mLock); mQueue.push_back(request); } -sp -ReportRequestQueue::getNextRequest() -{ +sp ReportRequestQueue::getNextRequest() { unique_lock lock(mLock); if (mQueue.empty()) { return NULL; @@ -110,22 +115,17 @@ ReportRequestQueue::getNextRequest() } } - // ================================================================================ -ReportHandler::ReportHandler(const sp& handlerLooper, const sp& queue) - :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), - mHandlerLooper(handlerLooper), - mQueue(queue) -{ -} +ReportHandler::ReportHandler(const sp& handlerLooper, const sp& queue, + const sp& throttler) + : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), + mHandlerLooper(handlerLooper), + mQueue(queue), + mThrottler(throttler) {} -ReportHandler::~ReportHandler() -{ -} +ReportHandler::~ReportHandler() {} -void -ReportHandler::handleMessage(const Message& message) -{ +void ReportHandler::handleMessage(const Message& message) { switch (message.what) { case WHAT_RUN_REPORT: run_report(); @@ -136,33 +136,24 @@ ReportHandler::handleMessage(const Message& message) } } -void -ReportHandler::scheduleRunReport(const sp& request) -{ +void ReportHandler::scheduleRunReport(const sp& request) { mQueue->addRequest(request); mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT); mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT)); } -void -ReportHandler::scheduleSendBacklogToDropbox() -{ +void ReportHandler::scheduleSendBacklogToDropbox() { unique_lock lock(mLock); mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; schedule_send_backlog_to_dropbox_locked(); } -void -ReportHandler::schedule_send_backlog_to_dropbox_locked() -{ +void ReportHandler::schedule_send_backlog_to_dropbox_locked() { mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX); - mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, - Message(WHAT_SEND_BACKLOG_TO_DROPBOX)); + mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BACKLOG_TO_DROPBOX)); } -void -ReportHandler::run_report() -{ +void ReportHandler::run_report() { sp reporter = new Reporter(); // Merge all of the requests into one that has all of the @@ -175,25 +166,30 @@ ReportHandler::run_report() reporter->batch.add(request); } + if (mThrottler->shouldThrottle()) { + ALOGW("RunReport got throttled."); + return; + } + // Take the report, which might take a while. More requests might queue // up while we're doing this, and we'll handle them in their next batch. // TODO: We should further rate-limit the reports to no more than N per time-period. - Reporter::run_report_status_t reportStatus = reporter->runReport(); + size_t reportByteSize = 0; + Reporter::run_report_status_t reportStatus = reporter->runReport(&reportByteSize); + mThrottler->addReportSize(reportByteSize); if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) { unique_lock lock(mLock); schedule_send_backlog_to_dropbox_locked(); } } -void -ReportHandler::send_backlog_to_dropbox() -{ +void ReportHandler::send_backlog_to_dropbox() { if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) { // There was a failure. Exponential backoff. unique_lock lock(mLock); mBacklogDelay *= 2; ALOGI("Error sending to dropbox. Trying again in %lld minutes", - (mBacklogDelay / (1000000000LL * 60))); + (mBacklogDelay / (1000000000LL * 60))); schedule_send_backlog_to_dropbox_locked(); } else { mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; @@ -202,18 +198,14 @@ ReportHandler::send_backlog_to_dropbox() // ================================================================================ IncidentService::IncidentService(const sp& handlerLooper) - :mQueue(new ReportRequestQueue()) -{ - mHandler = new ReportHandler(handlerLooper, mQueue); + : mQueue(new ReportRequestQueue()), + mThrottler(new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS)) { + mHandler = new ReportHandler(handlerLooper, mQueue, mThrottler); } -IncidentService::~IncidentService() -{ -} +IncidentService::~IncidentService() {} -Status -IncidentService::reportIncident(const IncidentReportArgs& args) -{ +Status IncidentService::reportIncident(const IncidentReportArgs& args) { ALOGI("reportIncident"); Status status = checkIncidentPermissions(args); @@ -226,10 +218,9 @@ IncidentService::reportIncident(const IncidentReportArgs& args) return Status::ok(); } -Status -IncidentService::reportIncidentToStream(const IncidentReportArgs& args, - const sp& listener, const unique_fd& stream) -{ +Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args, + const sp& listener, + const unique_fd& stream) { ALOGI("reportIncidentToStream"); Status status = checkIncidentPermissions(args); @@ -247,12 +238,10 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args, return Status::ok(); } -Status -IncidentService::systemRunning() -{ +Status IncidentService::systemRunning() { if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { return Status::fromExceptionCode(Status::EX_SECURITY, - "Only system uid can call systemRunning"); + "Only system uid can call systemRunning"); } // When system_server is up and running, schedule the dropbox task to run. @@ -261,3 +250,127 @@ IncidentService::systemRunning() return Status::ok(); } +/** + * Implement our own because the default binder implementation isn't + * properly handling SHELL_COMMAND_TRANSACTION. + */ +status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + status_t err; + + switch (code) { + case SHELL_COMMAND_TRANSACTION: { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(String8(data.readString16())); + } + sp shellCallback = IShellCallback::asInterface(data.readStrongBinder()); + sp resultReceiver = + IResultReceiver::asInterface(data.readStrongBinder()); + + FILE* fin = fdopen(in, "r"); + FILE* fout = fdopen(out, "w"); + FILE* ferr = fdopen(err, "w"); + + if (fin == NULL || fout == NULL || ferr == NULL) { + resultReceiver->send(NO_MEMORY); + } else { + err = command(fin, fout, ferr, args); + resultReceiver->send(err); + } + + if (fin != NULL) { + fflush(fin); + fclose(fin); + } + if (fout != NULL) { + fflush(fout); + fclose(fout); + } + if (fout != NULL) { + fflush(ferr); + fclose(ferr); + } + + return NO_ERROR; + } + default: { return BnIncidentManager::onTransact(code, data, reply, flags); } + } +} + +status_t IncidentService::command(FILE* in, FILE* out, FILE* err, Vector& args) { + const int argCount = args.size(); + + if (argCount >= 1) { + if (!args[0].compare(String8("privacy"))) { + return cmd_privacy(in, out, err, args); + } + if (!args[0].compare(String8("throttler"))) { + mThrottler->dump(out); + return NO_ERROR; + } + } + return cmd_help(out); +} + +status_t IncidentService::cmd_help(FILE* out) { + fprintf(out, "usage: adb shell cmd incident privacy print \n"); + fprintf(out, "usage: adb shell cmd incident privacy parse < proto.txt\n"); + fprintf(out, " Prints/parses for the section id.\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd incident throttler\n"); + fprintf(out, " Prints the current throttler state\n"); + return NO_ERROR; +} + +static void printPrivacy(const Privacy* p, FILE* out, String8 indent) { + if (p == NULL) return; + fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->dest); + if (p->children == NULL) return; + for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated. + printPrivacy(p->children[i], out, indent + " "); + } +} + +status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector& args) { + const int argCount = args.size(); + if (argCount >= 3) { + String8 opt = args[1]; + int sectionId = atoi(args[2].string()); + + const Privacy* p = get_privacy_of_section(sectionId); + if (p == NULL) { + fprintf(err, "Can't find section id %d\n", sectionId); + return NO_ERROR; + } + fprintf(err, "Get privacy for %d\n", sectionId); + if (opt == "print") { + printPrivacy(p, out, String8("")); + } else if (opt == "parse") { + FdBuffer buf; + status_t error = buf.read(fileno(in), 60000); + if (error != NO_ERROR) { + fprintf(err, "Error reading from stdin\n"); + return error; + } + fprintf(err, "Read %zu bytes\n", buf.size()); + auto data = buf.data(); + PrivacyBuffer pBuf(p, data); + + PrivacySpec spec = PrivacySpec::new_spec(argCount > 3 ? atoi(args[3]) : -1); + error = pBuf.strip(spec); + if (error != NO_ERROR) { + fprintf(err, "Error strip pii fields with spec %d\n", spec.dest); + return error; + } + return pBuf.flush(fileno(out)); + } + } else { + return cmd_help(out); + } + return NO_ERROR; +} diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h index d6f33dfb1a865567e4bdb9d5accdd97c565d0e14..0ab34ede96421f21360da613b1824f0dcd6773d9 100644 --- a/cmds/incidentd/src/IncidentService.h +++ b/cmds/incidentd/src/IncidentService.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef INCIDENT_SERVICE_H #define INCIDENT_SERVICE_H @@ -25,6 +26,8 @@ #include #include +#include "Throttler.h" + using namespace android; using namespace android::base; using namespace android::binder; @@ -32,8 +35,7 @@ using namespace android::os; using namespace std; // ================================================================================ -class ReportRequestQueue : public virtual RefBase -{ +class ReportRequestQueue : public virtual RefBase { public: ReportRequestQueue(); virtual ~ReportRequestQueue(); @@ -46,12 +48,11 @@ private: deque > mQueue; }; - // ================================================================================ -class ReportHandler : public MessageHandler -{ +class ReportHandler : public MessageHandler { public: - ReportHandler(const sp& handlerLooper, const sp& queue); + ReportHandler(const sp& handlerLooper, const sp& queue, + const sp& throttler); virtual ~ReportHandler(); virtual void handleMessage(const Message& message); @@ -72,6 +73,7 @@ private: nsecs_t mBacklogDelay; sp mHandlerLooper; sp mQueue; + sp mThrottler; /** * Runs all of the reports that have been queued. @@ -89,7 +91,6 @@ private: void send_backlog_to_dropbox(); }; - // ================================================================================ class IncidentService : public BnIncidentManager { public: @@ -99,14 +100,30 @@ public: virtual Status reportIncident(const IncidentReportArgs& args); virtual Status reportIncidentToStream(const IncidentReportArgs& args, - const sp& listener, const unique_fd& stream); + const sp& listener, + const unique_fd& stream); virtual Status systemRunning(); + // Implement commands for debugging purpose. + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) override; + virtual status_t command(FILE* in, FILE* out, FILE* err, Vector& args); + private: sp mQueue; sp mHandler; -}; + sp mThrottler; + /** + * Commands print out help. + */ + status_t cmd_help(FILE* out); + + /** + * Commands related to privacy filtering. + */ + status_t cmd_privacy(FILE* in, FILE* out, FILE* err, Vector& args); +}; -#endif // INCIDENT_SERVICE_H +#endif // INCIDENT_SERVICE_H diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/Log.h similarity index 52% rename from cmds/incidentd/src/io_util.cpp rename to cmds/incidentd/src/Log.h index 90f543e30ff77c3f9addcf2af34fc95612bb988d..22de46dfd9a2fb838139b8c7c4969ae313cf534e 100644 --- a/cmds/incidentd/src/io_util.cpp +++ b/cmds/incidentd/src/Log.h @@ -14,33 +14,20 @@ * limitations under the License. */ -#define LOG_TAG "incidentd" - -#include "io_util.h" - -#include - -status_t write_all(int fd, uint8_t const* buf, size_t size) -{ - while (size > 0) { - ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size)); - if (amt < 0) { - return -errno; - } - size -= amt; - buf += amt; - } - return NO_ERROR; -} - -Fpipe::Fpipe() {} - -Fpipe::~Fpipe() { close(); } +/* + * This file must be included at the top of the file. Other header files + * occasionally include log.h, and if LOG_TAG isn't set when that happens + * we'll get a preprocesser error when we try to define it here. + */ -bool Fpipe::close() { return !(::close(mFds[0]) || ::close(mFds[1])); } +#pragma once -bool Fpipe::init() { return pipe(mFds) != -1; } +#define LOG_TAG "incidentd" -int Fpipe::readFd() const { return mFds[0]; } +#include -int Fpipe::writeFd() const { return mFds[1]; } +// Use the local value to turn on/off debug logs instead of using log.tag.properties. +// The advantage is that in production compiler can remove the logging code if the local +// DEBUG/VERBOSE is false. +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__); \ No newline at end of file diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 3f0e331c8b55d39552d9942f54bf63aabcae0f85..c42a87b64a7389c36e0937174bb98d00a4bd321a 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -21,10 +21,9 @@ uint64_t encode_field_id(const Privacy* p) { return (uint64_t)p->type << 32 | p->field_id; } -const Privacy* lookup(const Privacy* p, uint32_t fieldId) -{ +const Privacy* lookup(const Privacy* p, uint32_t fieldId) { if (p->children == NULL) return NULL; - for (int i=0; p->children[i] != NULL; i++) { // NULL-terminated. + for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated. if (p->children[i]->field_id == fieldId) return p->children[i]; // Incident section gen tool guarantees field ids in ascending order. if (p->children[i]->field_id > fieldId) return NULL; @@ -32,52 +31,37 @@ const Privacy* lookup(const Privacy* p, uint32_t fieldId) return NULL; } -static bool allowDest(const uint8_t dest, const uint8_t policy) -{ +static bool allowDest(const uint8_t dest, const uint8_t policy) { switch (policy) { - case android::os::DEST_LOCAL: - return dest == android::os::DEST_LOCAL; - case android::os::DEST_EXPLICIT: - case DEST_UNSET: - return dest == android::os::DEST_LOCAL || - dest == android::os::DEST_EXPLICIT || - dest == DEST_UNSET; - case android::os::DEST_AUTOMATIC: - return true; - default: - return false; + case android::os::DEST_LOCAL: + return dest == android::os::DEST_LOCAL; + case android::os::DEST_EXPLICIT: + case DEST_UNSET: + return dest == android::os::DEST_LOCAL || dest == android::os::DEST_EXPLICIT || + dest == DEST_UNSET; + case android::os::DEST_AUTOMATIC: + return true; + default: + return false; } } -bool -PrivacySpec::operator<(const PrivacySpec& other) const -{ - return dest < other.dest; -} +bool PrivacySpec::operator<(const PrivacySpec& other) const { return dest < other.dest; } -bool -PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const -{ +bool PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const { uint8_t policy = privacy != NULL ? privacy->dest : defaultDest; return allowDest(dest, policy); } -bool -PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; } +bool PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; } -PrivacySpec PrivacySpec::new_spec(int dest) -{ +PrivacySpec PrivacySpec::new_spec(int dest) { switch (dest) { case android::os::DEST_AUTOMATIC: case android::os::DEST_EXPLICIT: case android::os::DEST_LOCAL: return PrivacySpec(dest); default: - return PrivacySpec(); + return PrivacySpec(android::os::DEST_AUTOMATIC); } } - -PrivacySpec PrivacySpec::get_default_dropbox_spec() -{ - return PrivacySpec(android::os::DEST_AUTOMATIC); -} diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h index 4f3db678f765d6928efda3e78b6d5ba87e1ef470..6b6de9cd9823fdb610484ed51fcfa0d24e13d2ee 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef PRIVACY_H #define PRIVACY_H @@ -20,7 +21,7 @@ #include // This is the default value of DEST enum, sync with privacy.proto -const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident +const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident const uint8_t DEST_DEFAULT_VALUE = DEST_UNSET; /* @@ -68,16 +69,17 @@ public: bool operator<(const PrivacySpec& other) const; // check permission of a policy, if returns true, don't strip the data. - bool CheckPremission(const Privacy* privacy, const uint8_t defaultDest = DEST_DEFAULT_VALUE) const; + bool CheckPremission(const Privacy* privacy, + const uint8_t defaultDest = DEST_DEFAULT_VALUE) const; // if returns true, no data need to be stripped. bool RequireAll() const; // Constructs spec using static methods below. static PrivacySpec new_spec(int dest); - static PrivacySpec get_default_dropbox_spec(); + private: PrivacySpec(uint8_t dest) : dest(dest) {} }; -#endif // PRIVACY_H +#endif // PRIVACY_H diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp index 03faa9290666a08206428049dedc35356530fdbc..ee57f4d0e5d6f154c14802d42b3812ce086ef9a3 100644 --- a/cmds/incidentd/src/PrivacyBuffer.cpp +++ b/cmds/incidentd/src/PrivacyBuffer.cpp @@ -13,29 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "PrivacyBuffer.h" -#include "io_util.h" +#include "incidentd_util.h" +#include #include #include using namespace android::util; -const bool DEBUG = false; - /** * Write the field to buf based on the wire type, iterator will point to next field. * If skip is set to true, no data will be written to buf. Return number of bytes written. */ -void -PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) -{ - if (DEBUG) ALOGD("%s field %d (wiretype = %d)", skip ? "skip" : "write", - read_field_id(fieldTag), read_wire_type(fieldTag)); - +void PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) { uint8_t wireType = read_wire_type(fieldTag); size_t bytesToWrite = 0; uint32_t varint = 0; @@ -54,18 +48,17 @@ PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) break; case WIRE_TYPE_LENGTH_DELIMITED: bytesToWrite = mData.readRawVarint(); - if(!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite); + if (!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite); break; case WIRE_TYPE_FIXED32: if (!skip) mProto.writeRawVarint(fieldTag); bytesToWrite = 4; break; } - if (DEBUG) ALOGD("%s %d bytes of data", skip ? "skip" : "write", (int)bytesToWrite); if (skip) { mData.rp()->move(bytesToWrite); } else { - for (size_t i=0; ichildren == NULL) { - if (DEBUG) ALOGD("Not a message field %d: dest(%d)", read_field_id(fieldTag), - policy != NULL ? policy->dest : parentPolicy->dest); - bool skip = !spec.CheckPremission(policy, parentPolicy->dest); // iterator will point to head of next field + size_t currentAt = mData.rp()->pos(); writeFieldOrSkip(fieldTag, skip); + VLOG("[Depth %2d]Field %d %ss %d bytes", depth, fieldId, skip ? "skip" : "write", + (int)(get_varint_size(fieldTag) + mData.rp()->pos() - currentAt)); return NO_ERROR; } // current field is message type and its sub-fields have extra privacy policies uint32_t msgSize = mData.readRawVarint(); - EncodedBuffer::Pointer start = mData.rp()->copy(); + size_t start = mData.rp()->pos(); long long token = mProto.start(encode_field_id(policy)); - while (mData.rp()->pos() - start.pos() != msgSize) { - status_t err = stripField(policy, spec); + while (mData.rp()->pos() - start != msgSize) { + status_t err = stripField(policy, spec, depth + 1); if (err != NO_ERROR) return err; } mProto.end(token); @@ -108,53 +102,39 @@ PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec) // ================================================================================ PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data) - :mPolicy(policy), - mData(data), - mProto(), - mSize(0) -{ -} + : mPolicy(policy), mData(data), mProto(), mSize(0) {} -PrivacyBuffer::~PrivacyBuffer() -{ -} +PrivacyBuffer::~PrivacyBuffer() {} -status_t -PrivacyBuffer::strip(const PrivacySpec& spec) -{ - if (DEBUG) ALOGD("Strip with spec %d", spec.dest); +status_t PrivacyBuffer::strip(const PrivacySpec& spec) { + VLOG("Strip with spec %d", spec.dest); // optimization when no strip happens if (mPolicy == NULL || mPolicy->children == NULL || spec.RequireAll()) { if (spec.CheckPremission(mPolicy)) mSize = mData.size(); return NO_ERROR; } while (mData.hasNext()) { - status_t err = stripField(mPolicy, spec); + status_t err = stripField(mPolicy, spec, 0); if (err != NO_ERROR) return err; } if (mData.bytesRead() != mData.size()) return BAD_VALUE; mSize = mProto.size(); - mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip. + mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip. return NO_ERROR; } -void -PrivacyBuffer::clear() -{ +void PrivacyBuffer::clear() { mSize = 0; - mProto = ProtoOutputStream(); + mProto.clear(); } -size_t -PrivacyBuffer::size() const { return mSize; } +size_t PrivacyBuffer::size() const { return mSize; } -status_t -PrivacyBuffer::flush(int fd) -{ +status_t PrivacyBuffer::flush(int fd) { status_t err = NO_ERROR; EncodedBuffer::iterator iter = size() == mData.size() ? mData : mProto.data(); while (iter.readBuffer() != NULL) { - err = write_all(fd, iter.readBuffer(), iter.currentToRead()); + err = WriteFully(fd, iter.readBuffer(), iter.currentToRead()) ? NO_ERROR : -errno; iter.rp()->move(iter.currentToRead()); if (err != NO_ERROR) return err; } diff --git a/cmds/incidentd/src/PrivacyBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h index c9ca9a752c9c42c34477cb46236886d01b86e132..92e1a2572465148287f19a509ae73bc4ffe738ce 100644 --- a/cmds/incidentd/src/PrivacyBuffer.h +++ b/cmds/incidentd/src/PrivacyBuffer.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef PRIVACY_BUFFER_H #define PRIVACY_BUFFER_H @@ -31,14 +32,14 @@ using namespace android::util; * PrivacyBuffer holds the original protobuf data and strips PII-sensitive fields * based on the request and holds stripped data in its own buffer for output. */ -class PrivacyBuffer -{ +class PrivacyBuffer { public: PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data); ~PrivacyBuffer(); /** - * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds. + * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip + * succeeds. */ status_t strip(const PrivacySpec& spec); @@ -64,8 +65,8 @@ private: ProtoOutputStream mProto; size_t mSize; - status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec); + status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec, int depth); void writeFieldOrSkip(uint32_t fieldTag, bool skip); }; -#endif // PRIVACY_BUFFER_H \ No newline at end of file +#endif // PRIVACY_BUFFER_H \ No newline at end of file diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index b9f479bd683f2010e444ad17942aca2afef1d61c..12764f8ff09c9a09f44745be6d5031bd724c8503 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "Reporter.h" +#include "Privacy.h" #include "report_directory.h" #include "section_list.h" @@ -25,11 +26,11 @@ #include #include -#include -#include #include -#include #include +#include +#include +#include /** * The directory where the incident reports are stored. @@ -38,72 +39,67 @@ static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/"; // ================================================================================ ReportRequest::ReportRequest(const IncidentReportArgs& a, - const sp &l, int f) - :args(a), - listener(l), - fd(f), - err(NO_ERROR) -{ -} + const sp& l, int f) + : args(a), listener(l), fd(f), err(NO_ERROR) {} -ReportRequest::~ReportRequest() -{ +ReportRequest::~ReportRequest() { if (fd >= 0) { // clean up the opened file descriptor close(fd); } } -bool -ReportRequest::ok() -{ - return fd >= 0 && err == NO_ERROR; -} +bool ReportRequest::ok() { return fd >= 0 && err == NO_ERROR; } // ================================================================================ ReportRequestSet::ReportRequestSet() - :mRequests(), - mSections(), - mMainFd(-1), - mMainDest(-1) -{ -} + : mRequests(), mSections(), mMainFd(-1), mMainDest(-1), mMetadata(), mSectionStats() {} -ReportRequestSet::~ReportRequestSet() -{ -} +ReportRequestSet::~ReportRequestSet() {} // TODO: dedup on exact same args and fd, report the status back to listener! -void -ReportRequestSet::add(const sp& request) -{ +void ReportRequestSet::add(const sp& request) { mRequests.push_back(request); mSections.merge(request->args); + mMetadata.set_request_size(mMetadata.request_size() + 1); } -void -ReportRequestSet::setMainFd(int fd) -{ +void ReportRequestSet::setMainFd(int fd) { mMainFd = fd; + mMetadata.set_use_dropbox(fd > 0); } -void -ReportRequestSet::setMainDest(int dest) -{ +void ReportRequestSet::setMainDest(int dest) { mMainDest = dest; + PrivacySpec spec = PrivacySpec::new_spec(dest); + switch (spec.dest) { + case android::os::DEST_AUTOMATIC: + mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC); + break; + case android::os::DEST_EXPLICIT: + mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT); + break; + case android::os::DEST_LOCAL: + mMetadata.set_dest(IncidentMetadata_Destination_LOCAL); + break; + } } -bool -ReportRequestSet::containsSection(int id) { - return mSections.containsSection(id); +bool ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); } + +IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) { + if (mSectionStats.find(id) == mSectionStats.end()) { + auto stats = mMetadata.add_sections(); + stats->set_id(id); + mSectionStats[id] = stats; + } + return mSectionStats[id]; } // ================================================================================ Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; }; -Reporter::Reporter(const char* directory) - :batch() -{ +Reporter::Reporter(const char* directory) : batch() { char buf[100]; // TODO: Make the max size smaller for user builds. @@ -121,22 +117,18 @@ Reporter::Reporter(const char* directory) mFilename = mIncidentDirectory + buf; } -Reporter::~Reporter() -{ -} - -Reporter::run_report_status_t -Reporter::runReport() -{ +Reporter::~Reporter() {} +Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) { status_t err = NO_ERROR; bool needMainFd = false; int mainFd = -1; int mainDest = -1; HeaderSection headers; + MetadataSection metadataSection; // See if we need the main file - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->fd < 0 && mainFd < 0) { needMainFd = true; mainDest = (*it)->args.dest(); @@ -167,7 +159,7 @@ Reporter::runReport() } // Tell everyone that we're starting. - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL) { (*it)->listener->onReportStarted(); } @@ -178,31 +170,36 @@ Reporter::runReport() // For each of the report fields, see if we need it, and if so, execute the command // and report to those that care that we're doing it. - for (const Section** section=SECTION_LIST; *section; section++) { + for (const Section** section = SECTION_LIST; *section; section++) { const int id = (*section)->id; if (this->batch.containsSection(id)) { ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string()); - // Notify listener of starting - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { - (*it)->listener->onReportSectionStatus(id, - IIncidentReportStatusListener::STATUS_STARTING); + (*it)->listener->onReportSectionStatus( + id, IIncidentReportStatusListener::STATUS_STARTING); } } // Execute - go get the data and write it into the file descriptors. + auto stats = batch.sectionStats(id); + int64_t startTime = uptimeMillis(); err = (*section)->Execute(&batch); + int64_t endTime = uptimeMillis(); + stats->set_success(err == NO_ERROR); + stats->set_exec_duration_ms(endTime - startTime); if (err != NO_ERROR) { ALOGW("Incident section %s (%d) failed: %s. Stopping report.", - (*section)->name.string(), id, strerror(-err)); + (*section)->name.string(), id, strerror(-err)); goto DONE; } + (*reportByteSize) += stats->report_size_bytes(); // Notify listener of starting - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { - (*it)->listener->onReportSectionStatus(id, - IIncidentReportStatusListener::STATUS_FINISHED); + (*it)->listener->onReportSectionStatus( + id, IIncidentReportStatusListener::STATUS_FINISHED); } } ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string()); @@ -210,13 +207,16 @@ Reporter::runReport() } DONE: + // Reports the metdadata when taking the incident report. + if (!isTest) metadataSection.Execute(&batch); + // Close the file. if (mainFd >= 0) { close(mainFd); } // Tell everyone that we're done. - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL) { if (err == NO_ERROR) { (*it)->listener->onReportFinished(); @@ -238,7 +238,7 @@ DONE: // If the status was ok, delete the file. If not, leave it around until the next // boot or the next checkin. If the directory gets too big older files will // be rotated out. - if(!isTest) unlink(mFilename.c_str()); + if (!isTest) unlink(mFilename.c_str()); } return REPORT_FINISHED; @@ -247,9 +247,7 @@ DONE: /** * Create our output file and set the access permissions to -rw-rw---- */ -status_t -Reporter::create_file(int* fd) -{ +status_t Reporter::create_file(int* fd) { const char* filename = mFilename.c_str(); *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660); @@ -271,9 +269,7 @@ Reporter::create_file(int* fd) return NO_ERROR; } -Reporter::run_report_status_t -Reporter::upload_backlog() -{ +Reporter::run_report_status_t Reporter::upload_backlog() { DIR* dir; struct dirent* entry; struct stat st; @@ -324,4 +320,3 @@ Reporter::upload_backlog() return REPORT_FINISHED; } - diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index f30ecf0dd648fd46fb2a7e8ce9d6371e18f16f50..ba8965ef4a6cc397a507393b6d12d88367edcb7f 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef REPORTER_H #define REPORTER_H @@ -20,33 +21,35 @@ #include #include +#include #include #include #include +#include "Throttler.h" +#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h" + using namespace android; using namespace android::os; using namespace std; // ================================================================================ -struct ReportRequest : public virtual RefBase -{ +struct ReportRequest : public virtual RefBase { IncidentReportArgs args; sp listener; int fd; status_t err; - ReportRequest(const IncidentReportArgs& args, - const sp &listener, int fd); + ReportRequest(const IncidentReportArgs& args, const sp& listener, + int fd); virtual ~ReportRequest(); - bool ok(); // returns true if the request is ok for write. + bool ok(); // returns true if the request is ok for write. }; // ================================================================================ -class ReportRequestSet -{ +class ReportRequestSet { public: ReportRequestSet(); ~ReportRequestSet(); @@ -61,32 +64,35 @@ public: iterator end() { return mRequests.end(); } int mainFd() { return mMainFd; } - bool containsSection(int id); int mainDest() { return mMainDest; } + IncidentMetadata& metadata() { return mMetadata; } + + bool containsSection(int id); + IncidentMetadata::SectionStats* sectionStats(int id); + private: vector> mRequests; IncidentReportArgs mSections; int mMainFd; int mMainDest; + + IncidentMetadata mMetadata; + map mSectionStats; }; // ================================================================================ -class Reporter : public virtual RefBase -{ +class Reporter : public virtual RefBase { public: - enum run_report_status_t { - REPORT_FINISHED = 0, - REPORT_NEEDS_DROPBOX = 1 - }; + enum run_report_status_t { REPORT_FINISHED = 0, REPORT_NEEDS_DROPBOX = 1 }; ReportRequestSet batch; - Reporter(); - Reporter(const char* directory); + Reporter(); // PROD must use this constructor. + Reporter(const char* directory); // For testing purpose only. virtual ~Reporter(); // Run the report as described in the batch and args parameters. - run_report_status_t runReport(); + run_report_status_t runReport(size_t* reportByteSize); static run_report_status_t upload_backlog(); @@ -100,8 +106,7 @@ private: status_t create_file(int* fd); - bool isTest = true; // default to true for testing + bool isTest = true; // default to true for testing }; - -#endif // REPORTER_H +#endif // REPORTER_H diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index faeab87f81780ae7bf74e45fbac64ffcae86ac11..64eae3acd34c1ae69827377c127c2ae45b7f890e 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "Section.h" @@ -26,42 +26,41 @@ #include #include +#include #include #include #include -#include #include +#include #include #include "FdBuffer.h" -#include "frameworks/base/core/proto/android/util/log.proto.h" -#include "io_util.h" #include "Privacy.h" #include "PrivacyBuffer.h" -#include "section_list.h" +#include "frameworks/base/core/proto/android/util/log.proto.h" +#include "incidentd_util.h" +using namespace android::base; using namespace android::util; using namespace std; // special section ids const int FIELD_ID_INCIDENT_HEADER = 1; +const int FIELD_ID_INCIDENT_METADATA = 2; // incident section parameters -const int WAIT_MAX = 5; +const int WAIT_MAX = 5; const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000}; const char INCIDENT_HELPER[] = "/system/bin/incident_helper"; -static pid_t -fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe) -{ - const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL }; +static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, + Fpipe& c2pPipe) { + const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL}; // fork used in multithreaded environment, avoid adding unnecessary code in child process pid_t pid = fork(); if (pid == 0) { - if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 - || !p2cPipe.close() - || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 - || !c2pPipe.close()) { + if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 || !p2cPipe.close() || + TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 || !c2pPipe.close()) { ALOGW("%s can't setup stdin and stdout for incident helper", name); _exit(EXIT_FAILURE); } @@ -72,7 +71,7 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi execv(INCIDENT_HELPER, const_cast(ihArgs)); ALOGW("%s failed in incident helper process: %s", name, strerror(errno)); - _exit(EXIT_FAILURE); // always exits with failure if any + _exit(EXIT_FAILURE); // always exits with failure if any } // close the fds used in incident helper close(p2cPipe.readFd()); @@ -83,18 +82,18 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi // ================================================================================ static status_t statusCode(int status) { if (WIFSIGNALED(status)) { - ALOGD("return by signal: %s", strerror(WTERMSIG(status))); - return -WTERMSIG(status); + VLOG("return by signal: %s", strerror(WTERMSIG(status))); + return -WTERMSIG(status); } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { - ALOGD("return by exit: %s", strerror(WEXITSTATUS(status))); - return -WEXITSTATUS(status); + VLOG("return by exit: %s", strerror(WEXITSTATUS(status))); + return -WEXITSTATUS(status); } return NO_ERROR; } static status_t kill_child(pid_t pid) { int status; - ALOGD("try to kill child process %d", pid); + VLOG("try to kill child process %d", pid); kill(pid, SIGKILL); if (waitpid(pid, &status, 0) == -1) return -1; return statusCode(status); @@ -104,7 +103,7 @@ static status_t wait_child(pid_t pid) { int status; bool died = false; // wait for child to report status up to 1 seconds - for(int loop = 0; !died && loop < WAIT_MAX; loop++) { + for (int loop = 0; !died && loop < WAIT_MAX; loop++) { if (waitpid(pid, &status, WNOHANG) == pid) died = true; // sleep for 0.2 second nanosleep(&WAIT_INTERVAL_NS, NULL); @@ -113,42 +112,24 @@ static status_t wait_child(pid_t pid) { return statusCode(status); } // ================================================================================ -static const Privacy* -get_privacy_of_section(int id) -{ - int l = 0; - int r = PRIVACY_POLICY_COUNT - 1; - while (l <= r) { - int mid = (l + r) >> 1; - const Privacy* p = PRIVACY_POLICY_LIST[mid]; - - if (p->field_id < (uint32_t)id) { - l = mid + 1; - } else if (p->field_id > (uint32_t)id) { - r = mid - 1; - } else { - return p; - } - } - return NULL; -} - -// ================================================================================ -static status_t -write_section_header(int fd, int sectionId, size_t size) -{ +static status_t write_section_header(int fd, int sectionId, size_t size) { uint8_t buf[20]; - uint8_t *p = write_length_delimited_tag_header(buf, sectionId, size); - return write_all(fd, buf, p-buf); + uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size); + return WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno; } -static status_t -write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests) -{ +static status_t write_report_requests(const int id, const FdBuffer& buffer, + ReportRequestSet* requests) { status_t err = -EBADF; EncodedBuffer::iterator data = buffer.data(); PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data); int writeable = 0; + auto stats = requests->sectionStats(id); + + stats->set_dump_size_bytes(data.size()); + stats->set_dump_duration_ms(buffer.durationMs()); + stats->set_timed_out(buffer.timedOut()); + stats->set_is_truncated(buffer.truncated()); // The streaming ones, group requests by spec in order to save unnecessary strip operations map>> requestsBySpec; @@ -164,38 +145,49 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) { PrivacySpec spec = mit->first; err = privacyBuffer.strip(spec); - if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted. + if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted. if (privacyBuffer.size() == 0) continue; for (auto it = mit->second.begin(); it != mit->second.end(); it++) { sp request = *it; err = write_section_header(request->fd, id, privacyBuffer.size()); - if (err != NO_ERROR) { request->err = err; continue; } + if (err != NO_ERROR) { + request->err = err; + continue; + } err = privacyBuffer.flush(request->fd); - if (err != NO_ERROR) { request->err = err; continue; } + if (err != NO_ERROR) { + request->err = err; + continue; + } writeable++; - ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, - privacyBuffer.size(), request->fd, spec.dest); + VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(), + request->fd, spec.dest); } privacyBuffer.clear(); } // The dropbox file if (requests->mainFd() >= 0) { - PrivacySpec spec = requests->mainDest() < 0 ? - PrivacySpec::get_default_dropbox_spec() : - PrivacySpec::new_spec(requests->mainDest()); + PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest()); err = privacyBuffer.strip(spec); - if (err != NO_ERROR) return err; // the buffer data is corrupted. + if (err != NO_ERROR) return err; // the buffer data is corrupted. if (privacyBuffer.size() == 0) goto DONE; err = write_section_header(requests->mainFd(), id, privacyBuffer.size()); - if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; } + if (err != NO_ERROR) { + requests->setMainFd(-1); + goto DONE; + } err = privacyBuffer.flush(requests->mainFd()); - if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; } + if (err != NO_ERROR) { + requests->setMainFd(-1); + goto DONE; + } writeable++; - ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id, - privacyBuffer.size(), requests->mainFd(), spec.dest); + VLOG("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(), + requests->mainFd(), spec.dest); + stats->set_report_size_bytes(privacyBuffer.size()); } DONE: @@ -204,67 +196,79 @@ DONE: } // ================================================================================ -Section::Section(int i, const int64_t timeoutMs) - :id(i), - timeoutMs(timeoutMs) -{ -} +Section::Section(int i, const int64_t timeoutMs) : id(i), timeoutMs(timeoutMs) {} -Section::~Section() -{ -} +Section::~Section() {} // ================================================================================ -HeaderSection::HeaderSection() - :Section(FIELD_ID_INCIDENT_HEADER, 0) -{ -} +HeaderSection::HeaderSection() : Section(FIELD_ID_INCIDENT_HEADER, 0) {} -HeaderSection::~HeaderSection() -{ -} +HeaderSection::~HeaderSection() {} -status_t -HeaderSection::Execute(ReportRequestSet* requests) const -{ - for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) { +status_t HeaderSection::Execute(ReportRequestSet* requests) const { + for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { const sp request = *it; const vector>& headers = request->args.headers(); - for (vector>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) { + for (vector>::const_iterator buf = headers.begin(); buf != headers.end(); + buf++) { if (buf->empty()) continue; // So the idea is only requests with negative fd are written to dropbox file. int fd = request->fd >= 0 ? request->fd : requests->mainFd(); - write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size()); - write_all(fd, (uint8_t const*)buf->data(), buf->size()); + write_section_header(fd, id, buf->size()); + WriteFully(fd, (uint8_t const*)buf->data(), buf->size()); // If there was an error now, there will be an error later and we will remove // it from the list then. } } return NO_ERROR; } +// ================================================================================ +MetadataSection::MetadataSection() : Section(FIELD_ID_INCIDENT_METADATA, 0) {} +MetadataSection::~MetadataSection() {} + +status_t MetadataSection::Execute(ReportRequestSet* requests) const { + std::string metadataBuf; + requests->metadata().SerializeToString(&metadataBuf); + for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { + const sp request = *it; + if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) { + continue; + } + write_section_header(request->fd, id, metadataBuf.size()); + if (!WriteFully(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size())) { + ALOGW("Failed to write metadata to fd %d", request->fd); + // we don't fail if we can't write to a single request's fd. + } + } + if (requests->mainFd() >= 0 && !metadataBuf.empty()) { + write_section_header(requests->mainFd(), id, metadataBuf.size()); + if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(), + metadataBuf.size())) { + ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd()); + return -1; + } + } + return NO_ERROR; +} // ================================================================================ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) - :Section(id, timeoutMs), - mFilename(filename) -{ + : Section(id, timeoutMs), mFilename(filename) { name = filename; mIsSysfs = strncmp(filename, "/sys/", 5) == 0; } FileSection::~FileSection() {} -status_t -FileSection::Execute(ReportRequestSet* requests) const -{ +status_t FileSection::Execute(ReportRequestSet* requests) const { // read from mFilename first, make sure the file is available // add O_CLOEXEC to make sure it is closed when exec incident helper int fd = open(mFilename, O_RDONLY | O_CLOEXEC); if (fd == -1) { - ALOGW("FileSection '%s' failed to open file", this->name.string()); - return -errno; + ALOGW("FileSection '%s' failed to open file", this->name.string()); + return -errno; } FdBuffer buffer; @@ -284,22 +288,23 @@ FileSection::Execute(ReportRequestSet* requests) const // parent process status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(), - this->timeoutMs, mIsSysfs); + this->timeoutMs, mIsSysfs); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); kill_child(pid); return readStatus; } status_t ihStatus = wait_child(pid); if (ihStatus != NO_ERROR) { - ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus)); + ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), + strerror(-ihStatus)); return ihStatus; } - ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), - (int)buffer.durationMs()); + VLOG("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), + (int)buffer.durationMs()); status_t err = write_report_requests(this->id, buffer, requests); if (err != NO_ERROR) { ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err)); @@ -310,8 +315,7 @@ FileSection::Execute(ReportRequestSet* requests) const } // ================================================================================ -struct WorkerThreadData : public virtual RefBase -{ +struct WorkerThreadData : public virtual RefBase { const WorkerThreadSection* section; int fds[2]; @@ -328,31 +332,19 @@ struct WorkerThreadData : public virtual RefBase }; WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec) - :section(sec), - workerDone(false), - workerError(NO_ERROR) -{ + : section(sec), workerDone(false), workerError(NO_ERROR) { fds[0] = -1; fds[1] = -1; } -WorkerThreadData::~WorkerThreadData() -{ -} +WorkerThreadData::~WorkerThreadData() {} // ================================================================================ -WorkerThreadSection::WorkerThreadSection(int id) - :Section(id) -{ -} +WorkerThreadSection::WorkerThreadSection(int id) : Section(id) {} -WorkerThreadSection::~WorkerThreadSection() -{ -} +WorkerThreadSection::~WorkerThreadSection() {} -static void* -worker_thread_func(void* cookie) -{ +static void* worker_thread_func(void* cookie) { WorkerThreadData* data = (WorkerThreadData*)cookie; status_t err = data->section->BlockingCall(data->writeFd()); @@ -368,9 +360,7 @@ worker_thread_func(void* cookie) return NULL; } -status_t -WorkerThreadSection::Execute(ReportRequestSet* requests) const -{ +status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { status_t err = NO_ERROR; pthread_t thread; pthread_attr_t attr; @@ -413,7 +403,7 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const if (err != NO_ERROR) { // TODO: Log this error into the incident report. ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(), - strerror(-err)); + strerror(-err)); } // Done with the read fd. The worker thread closes the write one so @@ -432,7 +422,7 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const err = data->workerError; // TODO: Log this error into the incident report. ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(), - strerror(-err)); + strerror(-err)); } } } @@ -450,13 +440,13 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const // just exit with a log messasge. if (err != NO_ERROR) { ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(), - strerror(-err)); + strerror(-err)); return NO_ERROR; } // Write the data that was collected - ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(), - (int)buffer.durationMs()); + VLOG("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(), + (int)buffer.durationMs()); err = write_report_requests(this->id, buffer, requests); if (err != NO_ERROR) { ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err)); @@ -467,14 +457,12 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const } // ================================================================================ -void -CommandSection::init(const char* command, va_list args) -{ +void CommandSection::init(const char* command, va_list args) { va_list copied_args; int numOfArgs = 0; va_copy(copied_args, args); - while(va_arg(copied_args, const char*) != NULL) { + while (va_arg(copied_args, const char*) != NULL) { numOfArgs++; } va_end(copied_args); @@ -484,41 +472,33 @@ CommandSection::init(const char* command, va_list args) mCommand[0] = command; name = command; - for (int i=0; iname.string(), strerror(errno)); + ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), + strerror(errno)); _exit(EXIT_FAILURE); } - execvp(this->mCommand[0], (char *const *) this->mCommand); - int err = errno; // record command error code - ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno)); - _exit(err); // exit with command error code + execvp(this->mCommand[0], (char* const*)this->mCommand); + int err = errno; // record command error code + ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), + strerror(errno)); + _exit(err); // exit with command error code } pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe); if (ihPid == -1) { @@ -555,24 +537,26 @@ CommandSection::Execute(ReportRequestSet* requests) const status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); kill_child(cmdPid); kill_child(ihPid); return readStatus; } - // TODO: wait for command here has one trade-off: the failed status of command won't be detected until + // TODO: wait for command here has one trade-off: the failed status of command won't be detected + // until // buffer timeout, but it has advatage on starting the data stream earlier. status_t cmdStatus = wait_child(cmdPid); - status_t ihStatus = wait_child(ihPid); + status_t ihStatus = wait_child(ihPid); if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) { - ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s", - this->name.string(), strerror(-cmdStatus), strerror(-ihStatus)); + ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident " + "helper: %s", + this->name.string(), strerror(-cmdStatus), strerror(-ihStatus)); return cmdStatus != NO_ERROR ? cmdStatus : ihStatus; } - ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), - (int)buffer.durationMs()); + VLOG("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), + (int)buffer.durationMs()); status_t err = write_report_requests(this->id, buffer, requests); if (err != NO_ERROR) { ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err)); @@ -583,9 +567,7 @@ CommandSection::Execute(ReportRequestSet* requests) const // ================================================================================ DumpsysSection::DumpsysSection(int id, const char* service, ...) - :WorkerThreadSection(id), - mService(service) -{ + : WorkerThreadSection(id), mService(service) { name = "dumpsys "; name += service; @@ -603,13 +585,9 @@ DumpsysSection::DumpsysSection(int id, const char* service, ...) va_end(args); } -DumpsysSection::~DumpsysSection() -{ -} +DumpsysSection::~DumpsysSection() {} -status_t -DumpsysSection::BlockingCall(int pipeWriteFd) const -{ +status_t DumpsysSection::BlockingCall(int pipeWriteFd) const { // checkService won't wait for the service to show up like getService will. sp service = defaultServiceManager()->checkService(mService); @@ -633,30 +611,23 @@ DumpsysSection::BlockingCall(int pipeWriteFd) const // initialization only once in Section.cpp. map LogSection::gLastLogsRetrieved; -LogSection::LogSection(int id, log_id_t logID) - :WorkerThreadSection(id), - mLogID(logID) -{ +LogSection::LogSection(int id, log_id_t logID) : WorkerThreadSection(id), mLogID(logID) { name += "logcat "; name += android_log_id_to_name(logID); switch (logID) { - case LOG_ID_EVENTS: - case LOG_ID_STATS: - case LOG_ID_SECURITY: - mBinary = true; - break; - default: - mBinary = false; + case LOG_ID_EVENTS: + case LOG_ID_STATS: + case LOG_ID_SECURITY: + mBinary = true; + break; + default: + mBinary = false; } } -LogSection::~LogSection() -{ -} +LogSection::~LogSection() {} -static size_t -trimTail(char const* buf, size_t len) -{ +static size_t trimTail(char const* buf, size_t len) { while (len > 0) { char c = buf[len - 1]; if (c == '\0' || c == ' ' || c == '\n' || c == '\r' || c == ':') { @@ -672,17 +643,15 @@ static inline int32_t get4LE(uint8_t const* src) { return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); } -status_t -LogSection::BlockingCall(int pipeWriteFd) const -{ +status_t LogSection::BlockingCall(int pipeWriteFd) const { status_t err = NO_ERROR; // Open log buffer and getting logs since last retrieved time if any. unique_ptr loggers( - gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() ? - android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) : - android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, - gLastLogsRetrieved[mLogID], 0), - android_logger_list_free); + gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() + ? android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) + : android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, + gLastLogsRetrieved[mLogID], 0), + android_logger_list_free); if (android_logger_open(loggers.get(), mLogID) == NULL) { ALOGW("LogSection %s: Can't get logger.", this->name.string()); @@ -693,7 +662,7 @@ LogSection::BlockingCall(int pipeWriteFd) const log_time lastTimestamp(0); ProtoOutputStream proto; - while (true) { // keeps reading until logd buffer is fully read. + while (true) { // keeps reading until logd buffer is fully read. status_t err = android_logger_list_read(loggers.get(), &msg); // err = 0 - no content, unexpected connection drop or EOF. // err = +ive number - size of retrieved data from logger @@ -708,7 +677,8 @@ LogSection::BlockingCall(int pipeWriteFd) const if (mBinary) { // remove the first uint32 which is tag's index in event log tags android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t), - msg.len() - sizeof(uint32_t));; + msg.len() - sizeof(uint32_t)); + ; android_log_list_element elem; lastTimestamp.tv_sec = msg.entry_v1.sec; @@ -718,38 +688,46 @@ LogSection::BlockingCall(int pipeWriteFd) const long long token = proto.start(LogProto::BINARY_LOGS); proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec); proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec); - proto.write(BinaryLogEntry::UID, (int) msg.entry_v4.uid); + proto.write(BinaryLogEntry::UID, (int)msg.entry_v4.uid); proto.write(BinaryLogEntry::PID, msg.entry_v1.pid); proto.write(BinaryLogEntry::TID, msg.entry_v1.tid); - proto.write(BinaryLogEntry::TAG_INDEX, get4LE(reinterpret_cast(msg.msg()))); + proto.write(BinaryLogEntry::TAG_INDEX, + get4LE(reinterpret_cast(msg.msg()))); do { elem = android_log_read_next(context); long long elemToken = proto.start(BinaryLogEntry::ELEMS); switch (elem.type) { case EVENT_TYPE_INT: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_INT); - proto.write(BinaryLogEntry::Elem::VAL_INT32, (int) elem.data.int32); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_INT); + proto.write(BinaryLogEntry::Elem::VAL_INT32, (int)elem.data.int32); break; case EVENT_TYPE_LONG: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LONG); - proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long) elem.data.int64); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_LONG); + proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long)elem.data.int64); break; case EVENT_TYPE_STRING: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_STRING); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_STRING); proto.write(BinaryLogEntry::Elem::VAL_STRING, elem.data.string, elem.len); break; case EVENT_TYPE_FLOAT: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_FLOAT); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_FLOAT); proto.write(BinaryLogEntry::Elem::VAL_FLOAT, elem.data.float32); break; case EVENT_TYPE_LIST: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_LIST); break; case EVENT_TYPE_LIST_STOP: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP); break; case EVENT_TYPE_UNKNOWN: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN); break; } proto.end(elemToken); @@ -777,7 +755,8 @@ LogSection::BlockingCall(int pipeWriteFd) const proto.write(TextLogEntry::PID, entry.pid); proto.write(TextLogEntry::TID, entry.tid); proto.write(TextLogEntry::TAG, entry.tag, trimTail(entry.tag, entry.tagLen)); - proto.write(TextLogEntry::LOG, entry.message, trimTail(entry.message, entry.messageLen)); + proto.write(TextLogEntry::LOG, entry.message, + trimTail(entry.message, entry.messageLen)); proto.end(token); } } diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index d440ee92601ca650123e39bdf86ede16bd40a485..d6446810c40fcabcda8a387d287f235700dd192a 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -13,31 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef SECTIONS_H #define SECTIONS_H #include "Reporter.h" -#include #include +#include -#include #include +#include #include using namespace android; -const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds +const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds /** * Base class for sections */ -class Section -{ +class Section { public: const int id; - const int64_t timeoutMs; // each section must have a timeout + const int64_t timeoutMs; // each section must have a timeout String8 name; Section(int id, const int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS); @@ -49,8 +49,7 @@ public: /** * Section that generates incident headers. */ -class HeaderSection : public Section -{ +class HeaderSection : public Section { public: HeaderSection(); virtual ~HeaderSection(); @@ -58,11 +57,21 @@ public: virtual status_t Execute(ReportRequestSet* requests) const; }; +/** + * Section that generates incident metadata. + */ +class MetadataSection : public Section { +public: + MetadataSection(); + virtual ~MetadataSection(); + + virtual status_t Execute(ReportRequestSet* requests) const; +}; + /** * Section that reads in a file. */ -class FileSection : public Section -{ +class FileSection : public Section { public: FileSection(int id, const char* filename, const int64_t timeoutMs = 5000 /* 5 seconds */); virtual ~FileSection(); @@ -71,14 +80,13 @@ public: private: const char* mFilename; - bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately + bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately }; /** * Base class for sections that call a command that might need a timeout. */ -class WorkerThreadSection : public Section -{ +class WorkerThreadSection : public Section { public: WorkerThreadSection(int id); virtual ~WorkerThreadSection(); @@ -91,8 +99,7 @@ public: /** * Section that forks and execs a command, and puts stdout as the section. */ -class CommandSection : public Section -{ +class CommandSection : public Section { public: CommandSection(int id, const int64_t timeoutMs, const char* command, ...); @@ -111,8 +118,7 @@ private: /** * Section that calls dumpsys on a system service. */ -class DumpsysSection : public WorkerThreadSection -{ +class DumpsysSection : public WorkerThreadSection { public: DumpsysSection(int id, const char* service, ...); virtual ~DumpsysSection(); @@ -127,8 +133,7 @@ private: /** * Section that reads from logd. */ -class LogSection : public WorkerThreadSection -{ +class LogSection : public WorkerThreadSection { // global last log retrieved timestamp for each log_id_t. static map gLastLogsRetrieved; @@ -143,5 +148,4 @@ private: bool mBinary; }; -#endif // SECTIONS_H - +#endif // SECTIONS_H diff --git a/cmds/incidentd/src/Throttler.cpp b/cmds/incidentd/src/Throttler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1abf26785733a39788555b5f1a31ffdefd57b2f6 --- /dev/null +++ b/cmds/incidentd/src/Throttler.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 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. + */ +#define DEBUG false +#include "Log.h" + +#include "Throttler.h" + +#include + +Throttler::Throttler(size_t limit, int64_t refractoryPeriodMs) + : mSizeLimit(limit), + mRefractoryPeriodMs(refractoryPeriodMs), + mAccumulatedSize(0), + mLastRefractoryMs(android::elapsedRealtime()) {} + +Throttler::~Throttler() {} + +bool Throttler::shouldThrottle() { + int64_t now = android::elapsedRealtime(); + if (now > mRefractoryPeriodMs + mLastRefractoryMs) { + mLastRefractoryMs = now; + mAccumulatedSize = 0; + } + return mAccumulatedSize > mSizeLimit; +} + +void Throttler::addReportSize(size_t reportByteSize) { + VLOG("The current request took %d bytes to dropbox", (int)reportByteSize); + mAccumulatedSize += reportByteSize; +} + +void Throttler::dump(FILE* out) { + fprintf(out, "mSizeLimit=%d\n", (int)mSizeLimit); + fprintf(out, "mAccumulatedSize=%d\n", (int)mAccumulatedSize); + fprintf(out, "mRefractoryPeriodMs=%d\n", (int)mRefractoryPeriodMs); + fprintf(out, "mLastRefractoryMs=%d\n", (int)mLastRefractoryMs); +} diff --git a/cmds/incidentd/src/Throttler.h b/cmds/incidentd/src/Throttler.h new file mode 100644 index 0000000000000000000000000000000000000000..c56f75386d492056d45570d528e94758021f47a5 --- /dev/null +++ b/cmds/incidentd/src/Throttler.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef THROTTLER_H +#define THROTTLER_H + +#include + +#include +/** + * This is a size-based throttler which prevents incidentd to take more data. + */ +class Throttler : public virtual android::RefBase { +public: + Throttler(size_t limit, int64_t refractoryPeriodMs); + ~Throttler(); + + /** + * Asserts this before starting taking report. + */ + bool shouldThrottle(); + + void addReportSize(size_t reportByteSize); + + void dump(FILE* out); + +private: + const size_t mSizeLimit; + const int64_t mRefractoryPeriodMs; + + size_t mAccumulatedSize; + int64_t mLastRefractoryMs; +}; + +#endif // THROTTLER_H diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2415860572fb22d18e9e13dc23cce183b516e03a --- /dev/null +++ b/cmds/incidentd/src/incidentd_util.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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. + */ +#include "incidentd_util.h" + +#include "section_list.h" + +const Privacy* get_privacy_of_section(int id) { + int l = 0; + int r = PRIVACY_POLICY_COUNT - 1; + while (l <= r) { + int mid = (l + r) >> 1; + const Privacy* p = PRIVACY_POLICY_LIST[mid]; + + if (p->field_id < (uint32_t)id) { + l = mid + 1; + } else if (p->field_id > (uint32_t)id) { + r = mid - 1; + } else { + return p; + } + } + return NULL; +} + +// ================================================================================ +Fpipe::Fpipe() : mRead(), mWrite() {} + +Fpipe::~Fpipe() { close(); } + +bool Fpipe::close() { + mRead.reset(); + mWrite.reset(); + return true; +} + +bool Fpipe::init() { return Pipe(&mRead, &mWrite); } + +int Fpipe::readFd() const { return mRead.get(); } + +int Fpipe::writeFd() const { return mWrite.get(); } \ No newline at end of file diff --git a/cmds/incidentd/src/io_util.h b/cmds/incidentd/src/incidentd_util.h similarity index 74% rename from cmds/incidentd/src/io_util.h rename to cmds/incidentd/src/incidentd_util.h index 320dd6c386d2810e45df1a16bae4b664e12cc7c0..09aa0404277ac50aae22f541d043c70f26496b1a 100644 --- a/cmds/incidentd/src/io_util.h +++ b/cmds/incidentd/src/incidentd_util.h @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once -#ifndef IO_UTIL_H -#define IO_UTIL_H +#ifndef INCIDENTD_UTIL_H +#define INCIDENTD_UTIL_H -#include -#include +#include -using namespace android; +#include "Privacy.h" -status_t write_all(int fd, uint8_t const* buf, size_t size); +using namespace android::base; + +const Privacy* get_privacy_of_section(int id); class Fpipe { public: @@ -35,7 +37,8 @@ public: int writeFd() const; private: - int mFds[2]; + unique_fd mRead; + unique_fd mWrite; }; -#endif // IO_UTIL_H \ No newline at end of file +#endif // INCIDENTD_UTIL_H \ No newline at end of file diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp index 3a7511d43048095e44e636be8c4faeae0788800f..38b7449403fe2b90b5b24dd07a3e056bb1859fb3 100644 --- a/cmds/incidentd/src/main.cpp +++ b/cmds/incidentd/src/main.cpp @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "IncidentService.h" @@ -23,25 +22,22 @@ #include #include #include -#include #include #include -#include #include +#include using namespace android; // ================================================================================ -int -main(int /*argc*/, char** /*argv*/) -{ +int main(int /*argc*/, char** /*argv*/) { // Set up the looper sp looper(Looper::prepare(0 /* opts */)); // Set up the binder sp ps(ProcessState::self()); - ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram + ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram ps->startThreadPool(); ps->giveThreadPoolName(); IPCThreadState::self()->disableBackgroundScheduling(true); diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp index 20111d8ae89a67ea4e75734f921c63736c548e81..f023ee1a6b7f60d73b901777f0572a753d7e5209 100644 --- a/cmds/incidentd/src/report_directory.cpp +++ b/cmds/incidentd/src/report_directory.cpp @@ -13,19 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "report_directory.h" -#include #include #include -#include -#include #include #include +#include +#include #include #include @@ -33,9 +32,7 @@ using namespace android; using namespace std; -status_t -create_directory(const char* directory) -{ +status_t create_directory(const char* directory) { struct stat st; status_t err = NO_ERROR; char* dir = strdup(directory); @@ -75,14 +72,14 @@ create_directory(const char* directory) goto done; } if ((st.st_mode & 0777) != 0770) { - ALOGE("No incident reports today. Mode is %0o on report directory %s", - st.st_mode, directory); + ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode, + directory); err = BAD_VALUE; goto done; } if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) { ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", - st.st_uid, st.st_gid, directory); + st.st_uid, st.st_gid, directory); err = BAD_VALUE; goto done; } @@ -92,20 +89,17 @@ done: return err; } -static bool -stat_mtime_cmp(const pair& a, const pair& b) -{ +static bool stat_mtime_cmp(const pair& a, + const pair& b) { return a.second.st_mtime < b.second.st_mtime; } -void -clean_directory(const char* directory, off_t maxSize, size_t maxCount) -{ +void clean_directory(const char* directory, off_t maxSize, size_t maxCount) { DIR* dir; struct dirent* entry; struct stat st; - vector> files; + vector> files; if ((dir = opendir(directory)) == NULL) { ALOGE("Couldn't open incident directory: %s", directory); @@ -131,7 +125,7 @@ clean_directory(const char* directory, off_t maxSize, size_t maxCount) if (!S_ISREG(st.st_mode)) { continue; } - files.push_back(pair(filename, st)); + files.push_back(pair(filename, st)); totalSize += st.st_size; totalCount++; @@ -148,8 +142,8 @@ clean_directory(const char* directory, off_t maxSize, size_t maxCount) sort(files.begin(), files.end(), stat_mtime_cmp); // Remove files until we're under our limits. - for (vector>::iterator it = files.begin(); - it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) { + for (vector>::iterator it = files.begin(); + it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) { remove(it->first.string()); totalSize -= it->second.st_size; totalCount--; diff --git a/cmds/incidentd/src/report_directory.h b/cmds/incidentd/src/report_directory.h index bed4f869cfe4663c8425e471ca3e1947c35a1204..2a3cf4c0f70114a12b2decde71323896823a3aba 100644 --- a/cmds/incidentd/src/report_directory.h +++ b/cmds/incidentd/src/report_directory.h @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef DIRECTORY_CLEANER_H #define DIRECTORY_CLEANER_H -#include - #include +#include -using namespace android; - -status_t create_directory(const char* directory); +android::status_t create_directory(const char* directory); void clean_directory(const char* directory, off_t maxSize, size_t maxCount); -#endif // DIRECTORY_CLEANER_H +#endif // DIRECTORY_CLEANER_H diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h index ddc0505df331ba5b4260b53b1f2a9c7d1ba79c9b..697e66f9cf40b110575e02a1ed62b8c910b9d424 100644 --- a/cmds/incidentd/src/section_list.h +++ b/cmds/incidentd/src/section_list.h @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef SECTION_LIST_H #define SECTION_LIST_H -#include // include log_id_t enums. +#include // include log_id_t enums. #include "Privacy.h" #include "Section.h" @@ -36,5 +37,4 @@ extern const Privacy** PRIVACY_POLICY_LIST; extern const int PRIVACY_POLICY_COUNT; -#endif // SECTION_LIST_H - +#endif // SECTION_LIST_H diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp index 3fd2ed82a26e31ae0184d62b50758ad261ded05a..0e5eec6c702302586dad59b308a188348baad7b7 100644 --- a/cmds/incidentd/tests/FdBuffer_test.cpp +++ b/cmds/incidentd/tests/FdBuffer_test.cpp @@ -11,11 +11,11 @@ // 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. - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "FdBuffer.h" -#include "io_util.h" +#include "incidentd_util.h" #include #include @@ -48,7 +48,7 @@ public: } void AssertBufferContent(const char* expected) { - int i=0; + int i = 0; EncodedBuffer::iterator it = buffer.data(); while (it.hasNext()) { ASSERT_EQ(it.next(), expected[i++]); @@ -100,7 +100,7 @@ TEST_F(FdBufferTest, ReadAndIterate) { ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); - int i=0; + int i = 0; EncodedBuffer::iterator it = buffer.data(); while (it.hasNext()) { EXPECT_EQ(it.next(), (uint8_t)testdata[i++]); @@ -118,7 +118,7 @@ TEST_F(FdBufferTest, ReadTimeout) { if (pid == 0) { close(c2pPipe.readFd()); - while(true) { + while (true) { write(c2pPipe.writeFd(), "poo", 3); sleep(1); } @@ -130,7 +130,7 @@ TEST_F(FdBufferTest, ReadTimeout) { ASSERT_EQ(NO_ERROR, status); EXPECT_TRUE(buffer.timedOut()); - kill(pid, SIGKILL); // reap the child process + kill(pid, SIGKILL); // reap the child process } } @@ -155,8 +155,8 @@ TEST_F(FdBufferTest, ReadInStreamAndWrite) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); AssertBufferReadSuccessful(HEAD.size() + testdata.size()); AssertBufferContent(expected.c_str()); wait(&pid); @@ -187,8 +187,8 @@ TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); AssertBufferReadSuccessful(HEAD.size() + testdata.size()); AssertBufferContent(expected.c_str()); wait(&pid); @@ -212,8 +212,8 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); AssertBufferReadSuccessful(0); AssertBufferContent(""); wait(&pid); @@ -222,7 +222,7 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { const std::string testFile = kTestDataPath + "morethan4MB.txt"; - size_t fourMB = (size_t) 4 * 1024 * 1024; + size_t fourMB = (size_t)4 * 1024 * 1024; int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC); ASSERT_NE(fd, -1); int pid = fork(); @@ -239,8 +239,8 @@ TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); EXPECT_EQ(buffer.size(), fourMB); EXPECT_FALSE(buffer.timedOut()); EXPECT_TRUE(buffer.truncated()); @@ -276,9 +276,9 @@ TEST_F(FdBufferTest, ReadInStreamTimeOut) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), QUICK_TIMEOUT_MS)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), QUICK_TIMEOUT_MS)); EXPECT_TRUE(buffer.timedOut()); - kill(pid, SIGKILL); // reap the child process + kill(pid, SIGKILL); // reap the child process } } diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp index c7bfe555574358711d38464f76b6c6bc4b5520f0..c7c69a746f0ac3bf6055915fce679d29db254b8c 100644 --- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp +++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp @@ -11,8 +11,8 @@ // 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. - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "FdBuffer.h" #include "PrivacyBuffer.h" @@ -37,13 +37,12 @@ const uint8_t OTHER_TYPE = 1; const uint8_t STRING_TYPE = 9; const uint8_t MESSAGE_TYPE = 11; const string STRING_FIELD_0 = "\x02\viamtestdata"; -const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 +const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 const string STRING_FIELD_2 = "\x12\vwhatthefuck"; -const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 -const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 +const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 +const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2; - class PrivacyBufferTest : public Test { public: virtual ~PrivacyBufferTest() { @@ -55,9 +54,7 @@ public: } } - virtual void SetUp() override { - ASSERT_NE(tf.fd, -1); - } + virtual void SetUp() override { ASSERT_NE(tf.fd, -1); } void writeToFdBuffer(string str) { ASSERT_TRUE(WriteStringToFile(str, tf.path)); @@ -81,11 +78,11 @@ public: } void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) { - Privacy* list[size+1]; + Privacy* list[size + 1]; list[0] = privacy; va_list args; va_start(args, privacy); - for (int i=1; i privacies; + std::vector privacies; - Privacy *new_uninit_privacy() { + Privacy* new_uninit_privacy() { Privacy* p = new Privacy; privacies.push_back(p); return p; @@ -165,63 +163,69 @@ TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) { TEST_F(PrivacyBufferTest, NoStripVarintField) { writeToFdBuffer(VARINT_FIELD_1); - assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, + create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) { writeToFdBuffer(STRING_FIELD_2); - assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, + create_privacy(2, STRING_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripFixed64Field) { writeToFdBuffer(FIX64_FIELD_3); - assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, + create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripFixed32Field) { writeToFdBuffer(FIX32_FIELD_4); - assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, + create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) { writeToFdBuffer(MESSAGE_FIELD_5); - assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, + create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, StripVarintAndString) { - writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 - + FIX64_FIELD_3 + FIX32_FIELD_4); + writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + + FIX32_FIELD_4); string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4; - assertStripByFields(DEST_EXPLICIT, expected, 2, - create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(2, STRING_TYPE, DEST_LOCAL)); + assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL), + create_privacy(2, STRING_TYPE, DEST_LOCAL)); } TEST_F(PrivacyBufferTest, StripVarintAndFixed64) { - writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 - + FIX64_FIELD_3 + FIX32_FIELD_4); + writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + + FIX32_FIELD_4); string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4; - assertStripByFields(DEST_EXPLICIT, expected, 2, - create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(3, OTHER_TYPE, DEST_LOCAL)); + assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL), + create_privacy(3, OTHER_TYPE, DEST_LOCAL)); } TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) { writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; assertStripByFields(DEST_EXPLICIT, expected, 1, create_message_privacy(5, list)); } TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) { writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; - assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), create_message_privacy(5, list)); + assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), + create_message_privacy(5, list)); } TEST_F(PrivacyBufferTest, ClearAndStrip) { string data = STRING_FIELD_0 + VARINT_FIELD_1; writeToFdBuffer(data); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; EncodedBuffer::iterator bufData = buffer.data(); PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData); PrivacySpec spec1 = PrivacySpec::new_spec(DEST_EXPLICIT); @@ -235,7 +239,7 @@ TEST_F(PrivacyBufferTest, ClearAndStrip) { TEST_F(PrivacyBufferTest, BadDataInFdBuffer) { writeToFdBuffer("iambaddata"); - Privacy* list[] = { create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL }; + Privacy* list[] = {create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL}; EncodedBuffer::iterator bufData = buffer.data(); PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData); PrivacySpec spec; @@ -244,8 +248,8 @@ TEST_F(PrivacyBufferTest, BadDataInFdBuffer) { TEST_F(PrivacyBufferTest, BadDataInNestedMessage) { writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe"); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; - Privacy* field5[] = { create_message_privacy(5, list), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; + Privacy* field5[] = {create_message_privacy(5, list), NULL}; EncodedBuffer::iterator bufData = buffer.data(); PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData); PrivacySpec spec; @@ -256,7 +260,7 @@ TEST_F(PrivacyBufferTest, SelfRecursionMessage) { string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5; writeToFdBuffer(input); Privacy* field5 = create_message_privacy(5, NULL); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL}; field5->children = list; string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2; assertStrip(DEST_EXPLICIT, expected, field5); @@ -264,7 +268,7 @@ TEST_F(PrivacyBufferTest, SelfRecursionMessage) { TEST_F(PrivacyBufferTest, AutoMessage) { writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC); autoMsg->children = list; string expected = "\x2a\xd" + STRING_FIELD_2; diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index c494bd646b0b282fcf4afa37c1cd5af3222b22c1..955dbac72ebe1ed8220f964ef2b3a16a9f0f1d58 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -11,8 +11,8 @@ // 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. - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "Reporter.h" @@ -26,7 +26,6 @@ #include #include - using namespace android; using namespace android::base; using namespace android::binder; @@ -35,8 +34,7 @@ using namespace std; using ::testing::StrEq; using ::testing::Test; -class TestListener : public IIncidentReportStatusListener -{ +class TestListener : public IIncidentReportStatusListener { public: int startInvoked; int finishInvoked; @@ -44,8 +42,8 @@ public: map startSections; map finishSections; - TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0) {}; - virtual ~TestListener() {}; + TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0){}; + virtual ~TestListener(){}; virtual Status onReportStarted() { startInvoked++; @@ -53,16 +51,14 @@ public: }; virtual Status onReportSectionStatus(int section, int status) { switch (status) { - case IIncidentReportStatusListener::STATUS_STARTING: - if (startSections.count(section) == 0) - startSections[section] = 0; - startSections[section] = startSections[section] + 1; - break; - case IIncidentReportStatusListener::STATUS_FINISHED: - if (finishSections.count(section) == 0) - finishSections[section] = 0; - finishSections[section] = finishSections[section] + 1; - break; + case IIncidentReportStatusListener::STATUS_STARTING: + if (startSections.count(section) == 0) startSections[section] = 0; + startSections[section] = startSections[section] + 1; + break; + case IIncidentReportStatusListener::STATUS_FINISHED: + if (finishSections.count(section) == 0) finishSections[section] = 0; + finishSections[section] = finishSections[section] + 1; + break; } return Status::ok(); }; @@ -111,6 +107,7 @@ protected: ReportRequestSet requests; sp reporter; sp l; + size_t size; }; TEST_F(ReporterTest, IncidentReportArgs) { @@ -130,7 +127,7 @@ TEST_F(ReporterTest, ReportRequestSetEmpty) { } TEST_F(ReporterTest, RunReportEmpty) { - ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); EXPECT_EQ(l->startInvoked, 0); EXPECT_EQ(l->finishInvoked, 0); EXPECT_TRUE(l->startSections.empty()); @@ -152,11 +149,12 @@ TEST_F(ReporterTest, RunReportWithHeaders) { reporter->batch.add(r1); reporter->batch.add(r2); - ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); string result; ReadFileToString(tf.path, &result); - EXPECT_THAT(result, StrEq("\n\x2" "\b\f")); + EXPECT_THAT(result, StrEq("\n\x2" + "\b\f")); EXPECT_EQ(l->startInvoked, 2); EXPECT_EQ(l->finishInvoked, 2); @@ -175,8 +173,27 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { sp r = new ReportRequest(args, l, -1); reporter->batch.add(r); - ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); vector results = InspectFiles(); ASSERT_EQ((int)results.size(), 1); - EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd"); + EXPECT_EQ(results[0], + "\n\x2" + "\b\f\n\x6" + "\x12\x4" + "abcd"); } + +TEST_F(ReporterTest, ReportMetadata) { + IncidentReportArgs args; + args.addSection(1); + args.setDest(android::os::DEST_EXPLICIT); + sp r = new ReportRequest(args, l, -1); + reporter->batch.add(r); + + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); + auto metadata = reporter->batch.metadata(); + EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest()); + EXPECT_EQ(1, metadata.request_size()); + EXPECT_TRUE(metadata.use_dropbox()); + EXPECT_EQ(0, metadata.sections_size()); +} \ No newline at end of file diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 2cfd7df6be846f1b05c468cb7a395bdb1b52372a..026bf7419eb1e002296bc1854936de1541f6dbc1 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -11,13 +11,14 @@ // 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. - -#define LOG_TAG "incidentd" +#define DEBUG false +#include "Log.h" #include "Section.h" #include #include +#include #include #include #include @@ -29,9 +30,9 @@ const int REVERSE_PARSER = 1; const int QUICK_TIMEOUT_MS = 100; -const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 +const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 const string STRING_FIELD_2 = "\x12\vwhatthefuck"; -const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 +const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 using namespace android::base; using namespace android::binder; @@ -43,11 +44,10 @@ using ::testing::internal::GetCapturedStdout; // NOTICE: this test requires /system/bin/incident_helper is installed. -class SimpleListener : public IIncidentReportStatusListener -{ +class SimpleListener : public IIncidentReportStatusListener { public: - SimpleListener() {}; - virtual ~SimpleListener() {}; + SimpleListener(){}; + virtual ~SimpleListener(){}; virtual Status onReportStarted() { return Status::ok(); }; virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); }; @@ -83,12 +83,26 @@ TEST(SectionTest, HeaderSection) { string content; CaptureStdout(); ASSERT_EQ(NO_ERROR, hs.Execute(&requests)); - EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup")); + EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" + "\x12\x3" + "axe\n\x05\x12\x03pup")); EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); } +TEST(SectionTest, MetadataSection) { + MetadataSection ms; + ReportRequestSet requests; + + requests.setMainFd(STDOUT_FILENO); + requests.sectionStats(1)->set_success(true); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, ms.Execute(&requests)); + EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1")); +} + TEST(SectionTest, FileSection) { TemporaryFile tf; FileSection fs(REVERSE_PARSER, tf.path); @@ -243,8 +257,9 @@ TEST(SectionTest, TestMultipleRequests) { IncidentReportArgs args1, args2, args3; args1.setAll(true); - args1.setDest(0); // LOCAL - args2.setAll(true); // default to explicit + args1.setDest(android::os::DEST_LOCAL); + args2.setAll(true); + args2.setDest(android::os::DEST_EXPLICIT); sp l = new SimpleListener(); requests.add(new ReportRequest(args1, l, output1.fd)); requests.add(new ReportRequest(args2, l, output2.fd)); @@ -257,12 +272,12 @@ TEST(SectionTest, TestMultipleRequests) { string content, expect; expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3; - char c = (char) expect.size(); + char c = (char)expect.size(); EXPECT_TRUE(ReadFileToString(output1.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); expect = STRING_FIELD_2 + FIX64_FIELD_3; - c = (char) expect.size(); + c = (char)expect.size(); EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); @@ -283,10 +298,12 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); - IncidentReportArgs args1, args2, args3, args4; + IncidentReportArgs args1, args2, args3; args1.setAll(true); + args1.setDest(android::os::DEST_EXPLICIT); args2.setAll(true); - args4.setAll(true); + args2.setDest(android::os::DEST_EXPLICIT); + args3.setAll(true); sp l = new SimpleListener(); requests.add(new ReportRequest(args1, l, output1.fd)); requests.add(new ReportRequest(args2, l, output2.fd)); @@ -299,7 +316,7 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { string content, expect; expect = STRING_FIELD_2 + FIX64_FIELD_3; - char c = (char) expect.size(); + char c = (char)expect.size(); // output1 and output2 are the same EXPECT_TRUE(ReadFileToString(output1.path, &content)); @@ -307,7 +324,8 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); - // because args3 doesn't set section, so it should receive nothing + // output3 has only auto field + c = (char)STRING_FIELD_2.size(); EXPECT_TRUE(ReadFileToString(output3.path, &content)); - EXPECT_THAT(content, StrEq("")); + EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2)); } \ No newline at end of file diff --git a/cmds/incidentd/tests/Throttler_test.cpp b/cmds/incidentd/tests/Throttler_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..213dcef5d30aebb84b918db751daed00e1a0fe27 --- /dev/null +++ b/cmds/incidentd/tests/Throttler_test.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2018 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. +#define DEBUG false +#include "Log.h" + +#include "Throttler.h" + +#include +#include +#include + +TEST(ThrottlerTest, DataSizeExceeded) { + Throttler t(100, 100000); + EXPECT_FALSE(t.shouldThrottle()); + t.addReportSize(200); + EXPECT_TRUE(t.shouldThrottle()); +} + +TEST(ThrottlerTest, TimeReset) { + Throttler t(100, 500); + EXPECT_FALSE(t.shouldThrottle()); + t.addReportSize(200); + EXPECT_TRUE(t.shouldThrottle()); + sleep(1); // sleep for 1 second to make sure throttler resets + EXPECT_FALSE(t.shouldThrottle()); +} diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp index 1d6213fd19b3902f3d84cbcdeac904707550afa6..bd2d15cc38be98a3f3aaeedf7f31be78c5d4dbe3 100644 --- a/cmds/incidentd/tests/section_list.cpp +++ b/cmds/incidentd/tests/section_list.cpp @@ -1,25 +1,17 @@ // This file is a dummy section_list.cpp used for test only. #include "section_list.h" -const Section* SECTION_LIST[] = { - NULL -}; +const Section* SECTION_LIST[] = {NULL}; -Privacy sub_field_1 { 1, 1, NULL, DEST_LOCAL, NULL }; -Privacy sub_field_2 { 2, 9, NULL, DEST_AUTOMATIC, NULL }; +Privacy sub_field_1{1, 1, NULL, DEST_LOCAL, NULL}; +Privacy sub_field_2{2, 9, NULL, DEST_AUTOMATIC, NULL}; -Privacy* list[] = { - &sub_field_1, - &sub_field_2, - NULL }; +Privacy* list[] = {&sub_field_1, &sub_field_2, NULL}; -Privacy field_0 { 0, 11, list, DEST_EXPLICIT, NULL }; -Privacy field_1 { 1, 9, NULL, DEST_AUTOMATIC, NULL }; +Privacy field_0{0, 11, list, DEST_EXPLICIT, NULL}; +Privacy field_1{1, 9, NULL, DEST_AUTOMATIC, NULL}; -Privacy* final_list[] = { - &field_0, - &field_1 -}; +Privacy* final_list[] = {&field_0, &field_1}; const Privacy** PRIVACY_POLICY_LIST = const_cast(final_list); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index a5eae15b50da7bd4cd3562b7302f422e5dffe9a4..b5660995fa369cd717c1d464b2996f0ac69ed12c 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -21,6 +21,7 @@ cc_library_host_shared { name: "libstats_proto_host", srcs: [ "src/atoms.proto", + "src/atom_field_options.proto", ], shared_libs: [ @@ -30,6 +31,9 @@ cc_library_host_shared { proto: { type: "full", export_proto_headers: true, + include_dirs: [ + "external/protobuf/src", + ], }, export_shared_lib_headers: [ diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index b0019ac90708b58fb3e2fc16e68e4f55afbed45f..87825f1619702830623ad9fd1e0ac05e2792538b 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -17,13 +17,10 @@ LOCAL_PATH:= $(call my-dir) statsd_common_src := \ ../../core/java/android/os/IStatsCompanionService.aidl \ ../../core/java/android/os/IStatsManager.aidl \ - src/stats_log.proto \ + src/stats_log_common.proto \ src/statsd_config.proto \ - src/statsd_internal.proto \ - src/atoms.proto \ - src/field_util.cpp \ + src/FieldValue.cpp \ src/stats_log_util.cpp \ - src/dimension.cpp \ src/anomaly/AnomalyMonitor.cpp \ src/anomaly/AnomalyTracker.cpp \ src/anomaly/DurationAnomalyTracker.cpp \ @@ -44,6 +41,7 @@ statsd_common_src := \ src/external/KernelUidCpuActiveTimeReader.cpp \ src/external/KernelUidCpuClusterTimeReader.cpp \ src/external/StatsPullerManagerImpl.cpp \ + src/external/puller_util.cpp \ src/logd/LogEvent.cpp \ src/logd/LogListener.cpp \ src/logd/LogReader.cpp \ @@ -65,6 +63,7 @@ statsd_common_src := \ src/storage/StorageManager.cpp \ src/StatsLogProcessor.cpp \ src/StatsService.cpp \ + src/subscriber/IncidentdReporter.cpp \ src/subscriber/SubscriberReporter.cpp \ src/HashableDimensionKey.cpp \ src/guardrail/MemoryLeakTrackUtil.cpp \ @@ -116,10 +115,8 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS += \ -Wall \ + -Wextra \ -Werror \ - -Wno-missing-field-initializers \ - -Wno-unused-variable \ - -Wno-unused-function \ -Wno-unused-parameter ifeq (debug,) @@ -170,10 +167,13 @@ LOCAL_CFLAGS += \ LOCAL_SRC_FILES := \ $(statsd_common_src) \ - tests/dimension_test.cpp \ + src/atom_field_options.proto \ + src/atoms.proto \ + src/stats_log.proto \ tests/AnomalyMonitor_test.cpp \ tests/anomaly/AnomalyTracker_test.cpp \ tests/ConfigManager_test.cpp \ + tests/external/puller_util_test.cpp \ tests/indexed_priority_queue_test.cpp \ tests/LogEntryMatcher_test.cpp \ tests/LogReader_test.cpp \ @@ -181,6 +181,7 @@ LOCAL_SRC_FILES := \ tests/MetricsManager_test.cpp \ tests/StatsLogProcessor_test.cpp \ tests/UidMap_test.cpp \ + tests/FieldValue_test.cpp \ tests/condition/CombinationConditionTracker_test.cpp \ tests/condition/SimpleConditionTracker_test.cpp \ tests/metrics/OringDurationTracker_test.cpp \ @@ -203,9 +204,13 @@ LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) \ libgmock -LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) +LOCAL_PROTOC_OPTIMIZE_TYPE := full -LOCAL_PROTOC_OPTIMIZE_TYPE := lite +LOCAL_PROTOC_FLAGS := \ + -Iexternal/protobuf/src + +LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ + libprotobuf-cpp-full include $(BUILD_NATIVE_TEST) @@ -218,6 +223,7 @@ LOCAL_MODULE := statsdprotolite LOCAL_SRC_FILES := \ src/stats_log.proto \ + src/stats_log_common.proto \ src/statsd_config.proto \ src/perfetto/perfetto_config.proto \ src/atoms.proto @@ -227,6 +233,9 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_STATIC_JAVA_LIBRARIES := \ platformprotoslite +LOCAL_PROTOC_FLAGS := \ + -Iexternal/protobuf/src + include $(BUILD_STATIC_JAVA_LIBRARY) ############################## @@ -239,7 +248,9 @@ LOCAL_MODULE := statsd_benchmark LOCAL_SRC_FILES := $(statsd_common_src) \ benchmark/main.cpp \ benchmark/hello_world_benchmark.cpp \ - benchmark/log_event_benchmark.cpp + benchmark/log_event_benchmark.cpp \ + benchmark/stats_write_benchmark.cpp \ + benchmark/filter_value_benchmark.cpp LOCAL_C_INCLUDES := $(statsd_common_c_includes) @@ -258,9 +269,8 @@ LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ - libgtest_prod - -LOCAL_PROTOC_OPTIMIZE_TYPE := lite + libgtest_prod \ + libstatslog LOCAL_MODULE_TAGS := eng tests diff --git a/cmds/statsd/AndroidTest.xml b/cmds/statsd/AndroidTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..afe30a26909361e5f34e11d77104e833a87da755 --- /dev/null +++ b/cmds/statsd/AndroidTest.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..362d411d03a5a433a9e2d613fbb38dc2ee938137 --- /dev/null +++ b/cmds/statsd/OWNERS @@ -0,0 +1,6 @@ +bookatz@google.com +jinyithu@google.com +kwekua@google.com +stlafon@google.com +yaochen@google.com +yanglu@google.com diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9ddf36d82955cbbc89c0d897e696ec31035c822 --- /dev/null +++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 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. + */ +#include +#include "benchmark/benchmark.h" +#include "FieldValue.h" +#include "HashableDimensionKey.h" +#include "logd/LogEvent.h" + +namespace android { +namespace os { +namespace statsd { + +using std::vector; + +static void BM_FilterValue(benchmark::State& state) { + LogEvent event(1, 100000); + event.write(3.2f); + event.write("LOCATION"); + event.write((int64_t)990); + event.init(); + + FieldMatcher field_matcher; + field_matcher.set_field(1); + field_matcher.add_child()->set_field(2); + field_matcher.add_child()->set_field(3); + + std::vector matchers; + translateFieldMatcher(field_matcher, &matchers); + + while (state.KeepRunning()) { + vector output; + filterValues(matchers, event.getValues(), &output); + } +} + +BENCHMARK(BM_FilterValue); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/benchmark/stats_write_benchmark.cpp b/cmds/statsd/benchmark/stats_write_benchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5a0cd5dfb3984017fac33b10a6a1767d1758d82 --- /dev/null +++ b/cmds/statsd/benchmark/stats_write_benchmark.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 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. + */ +#include "benchmark/benchmark.h" +#include + +namespace android { +namespace os { +namespace statsd { + +static void BM_StatsWrite(benchmark::State& state) { + const char* reason = "test"; + int64_t boot_end_time = 1234567; + int64_t total_duration = 100; + int64_t bootloader_duration = 10; + int64_t time_since_last_boot = 99999999; + while (state.KeepRunning()) { + android::util::stats_write( + android::util::BOOT_SEQUENCE_REPORTED, reason, reason, + boot_end_time, total_duration, bootloader_duration, time_since_last_boot); + total_duration++; + } +} +BENCHMARK(BM_StatsWrite); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b541612f73df39e7002aef18e1bc707d4ae9c85e --- /dev/null +++ b/cmds/statsd/src/FieldValue.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2018 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. + */ + +#define DEBUG false +#include "Log.h" +#include "FieldValue.h" +#include "HashableDimensionKey.h" + +namespace android { +namespace os { +namespace statsd { + +int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) { + int32_t field = 0; + for (int32_t i = 0; i <= depth; i++) { + int32_t shiftBits = 8 * (kMaxLogDepth - i); + field |= (pos[i] << shiftBits); + } + + if (includeDepth) { + field |= (depth << 24); + } + return field; +} + +int32_t encodeMatcherMask(int32_t mask[], int32_t depth) { + return getEncodedField(mask, depth, false) | 0xff000000; +} + +bool Field::matches(const Matcher& matcher) const { + if (mTag != matcher.mMatcher.getTag()) { + return false; + } + if ((mField & matcher.mMask) == matcher.mMatcher.getField()) { + return true; + } + + return false; +} + +void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask, + std::vector* output) { + if (depth > kMaxLogDepth) { + ALOGE("depth > 2"); + return; + } + + pos[depth] = matcher.field(); + mask[depth] = 0x7f; + + if (matcher.has_position()) { + depth++; + if (depth > 2) { + return; + } + switch (matcher.position()) { + case Position::ANY: + pos[depth] = 0; + mask[depth] = 0; + break; + case Position::FIRST: + pos[depth] = 1; + mask[depth] = 0x7f; + break; + case Position::LAST: + pos[depth] = 0x80; + mask[depth] = 0x80; + break; + case Position::POSITION_UNKNOWN: + pos[depth] = 0; + mask[depth] = 0; + break; + } + } + + if (matcher.child_size() == 0) { + output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth))); + } else { + for (const auto& child : matcher.child()) { + translateFieldMatcher(tag, child, depth + 1, pos, mask, output); + } + } +} + +void translateFieldMatcher(const FieldMatcher& matcher, std::vector* output) { + int pos[] = {1, 1, 1}; + int mask[] = {0x7f, 0x7f, 0x7f}; + int tag = matcher.field(); + for (const auto& child : matcher.child()) { + translateFieldMatcher(tag, child, 0, pos, mask, output); + } +} + +bool isAttributionUidField(const FieldValue& value) { + int field = value.mField.getField() & 0xff007f; + if (field == 0x10001 && value.mValue.getType() == INT) { + return true; + } + return false; +} + +bool isAttributionUidField(const Field& field, const Value& value) { + int f = field.getField() & 0xff007f; + if (f == 0x10001 && value.getType() == INT) { + return true; + } + return false; +} + +Value::Value(const Value& from) { + type = from.getType(); + switch (type) { + case INT: + int_value = from.int_value; + break; + case LONG: + long_value = from.long_value; + break; + case FLOAT: + float_value = from.float_value; + break; + case STRING: + str_value = from.str_value; + break; + default: + break; + } +} + +std::string Value::toString() const { + switch (type) { + case INT: + return std::to_string(int_value) + "[I]"; + case LONG: + return std::to_string(long_value) + "[L]"; + case FLOAT: + return std::to_string(float_value) + "[F]"; + case STRING: + return str_value + "[S]"; + default: + return "[UNKNOWN]"; + } +} + +bool Value::operator==(const Value& that) const { + if (type != that.getType()) return false; + + switch (type) { + case INT: + return int_value == that.int_value; + case LONG: + return long_value == that.long_value; + case FLOAT: + return float_value == that.float_value; + case STRING: + return str_value == that.str_value; + default: + return false; + } +} + +bool Value::operator!=(const Value& that) const { + if (type != that.getType()) return true; + switch (type) { + case INT: + return int_value != that.int_value; + case LONG: + return long_value != that.long_value; + case FLOAT: + return float_value != that.float_value; + case STRING: + return str_value != that.str_value; + default: + return false; + } +} + +bool Value::operator<(const Value& that) const { + if (type != that.getType()) return type < that.getType(); + + switch (type) { + case INT: + return int_value < that.int_value; + case LONG: + return long_value < that.long_value; + case FLOAT: + return float_value < that.float_value; + case STRING: + return str_value < that.str_value; + default: + return false; + } +} + +} // namespace statsd +} // namespace os +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h new file mode 100644 index 0000000000000000000000000000000000000000..21f30e288c257b3c0a2c9d27e62bd5bdce69bf20 --- /dev/null +++ b/cmds/statsd/src/FieldValue.h @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2018 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. + */ +#pragma once + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +class HashableDimensionKey; +struct Matcher; +struct Field; +struct FieldValue; + +const int32_t kAttributionField = 1; +const int32_t kMaxLogDepth = 2; +const int32_t kLastBitMask = 0x80; +const int32_t kClearLastBitDeco = 0x7f; + +enum Type { UNKNOWN, INT, LONG, FLOAT, STRING }; + +int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth); + +int32_t encodeMatcherMask(int32_t mask[], int32_t depth); + +// Get the encoded field for a leaf with a [field] number at depth 0; +inline int32_t getSimpleField(size_t field) { + return ((int32_t)field << 8 * 2); +} +/** + * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom + * proto. + * [mTag]: the atom id. + * [mField]: encoded path from the root (atom) to leaf. + * + * For example: + * WakeLockStateChanged { + * repeated AttributionNode = 1; + * int state = 2; + * string tag = 3; + * } + * Read from logd, the items are structured as below: + * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"] + * + * When we read through the list, we will encode each field in a 32bit integer. + * 8bit segments |--------|--------|--------|--------| + * Depth field0 [L]field1 [L]field1 + * + * The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2. + * The following 3 8-bit are for the item's position at each level. + * The first bit of each 8bits field is reserved to mark if the item is the last item at that level + * this is to make matching easier later. + * + * The above wakelock event is translated into FieldValue pairs. + * 0x02010101->1000 + * 0x02010182->tag + * 0x02018201->2000 + * 0x02018282->tag2 + * 0x00020000->2 + * 0x00030000->"hello" + * + * This encoding is the building block for the later operations. + * Please see the definition for Matcher below to see how the matching is done. + */ +struct Field { +private: + int32_t mTag; + int32_t mField; + +public: + Field() {} + + Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) { + mField = getEncodedField(pos, depth, true); + } + + Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) { + } + + Field(int32_t tag, int32_t field) : mTag(tag), mField(field){}; + + inline void setField(int32_t field) { + mField = field; + } + + inline void setTag(int32_t tag) { + mTag = tag; + } + + inline void decorateLastPos(int32_t depth) { + int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); + mField |= mask; + } + + inline int32_t getTag() const { + return mTag; + } + + inline int32_t getDepth() const { + return (mField >> 24); + } + + inline int32_t getPath(int32_t depth) const { + if (depth > 2 || depth < 0) return 0; + + int32_t field = (mField & 0x00ffffff); + int32_t mask = 0xffffffff; + return (field & (mask << 8 * (kMaxLogDepth - depth))); + } + + inline int32_t getPrefix(int32_t depth) const { + if (depth == 0) return 0; + return getPath(depth - 1); + } + + inline int32_t getField() const { + return mField; + } + + inline int32_t getRawPosAtDepth(int32_t depth) const { + int32_t field = (mField & 0x00ffffff); + int32_t shift = 8 * (kMaxLogDepth - depth); + int32_t mask = 0xff << shift; + + return (field & mask) >> shift; + } + + inline int32_t getPosAtDepth(int32_t depth) const { + return getRawPosAtDepth(depth) & kClearLastBitDeco; + } + + // Check if the first bit of the 8-bit segment for depth is 1 + inline bool isLastPos(int32_t depth) const { + int32_t field = (mField & 0x00ffffff); + int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth); + return (field & mask) != 0; + } + + // if the 8-bit segment is all 0's + inline bool isAnyPosMatcher(int32_t depth) const { + return getDepth() >= depth && getRawPosAtDepth(depth) == 0; + } + // if the 8bit is 0x80 (1000 0000) + inline bool isLastPosMatcher(int32_t depth) const { + return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask; + } + + inline bool operator==(const Field& that) const { + return mTag == that.getTag() && mField == that.getField(); + }; + + inline bool operator!=(const Field& that) const { + return mTag != that.getTag() || mField != that.getField(); + }; + + bool operator<(const Field& that) const { + if (mTag != that.getTag()) { + return mTag < that.getTag(); + } + + if (mField != that.getField()) { + return mField < that.getField(); + } + + return false; + } + bool matches(const Matcher& that) const; +}; + +/** + * Matcher represents a leaf matcher in the FieldMatcher in statsd_config. + * + * It contains all information needed to match one or more leaf node. + * All information is encoded in a Field(2 ints) and a bit mask(1 int). + * + * For example, to match the first/any/last uid field in attribution chain in Atom 10, + * we have the following FieldMatcher in statsd_config + * FieldMatcher { + * field:10 + * FieldMatcher { + * field:1 + * position: any/last/first + * FieldMatcher { + * field:1 + * } + * } + * } + * + * We translate the FieldMatcher into a Field, and mask + * First: [Matcher Field] 0x02010101 [Mask]0xffff7fff + * Last: [Matcher Field] 0x02018001 [Mask]0xffff80ff + * Any: [Matcher Field] 0x02010001 [Mask]0xffff00ff + * + * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if + * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are + * equal. Nothing can beat the performance of this matching algorithm. + * + * TODO: ADD EXAMPLE HERE. + */ +struct Matcher { + Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){}; + + const Field mMatcher; + const int32_t mMask; + + bool hasAnyPositionMatcher(int* prefix) const { + if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) { + (*prefix) = mMatcher.getPrefix(2); + return true; + } + return false; + } +}; + +/** + * A wrapper for a union type to contain multiple types of values. + * + */ +struct Value { + Value() : type(UNKNOWN) {} + + Value(int32_t v) { + int_value = v; + type = INT; + } + + Value(int64_t v) { + long_value = v; + type = LONG; + } + + Value(float v) { + float_value = v; + type = FLOAT; + } + + Value(const std::string& v) { + str_value = v; + type = STRING; + } + + void setInt(int32_t v) { + int_value = v; + type = INT; + } + + void setLong(int64_t v) { + long_value = v; + type = LONG; + } + + union { + int32_t int_value; + int64_t long_value; + float float_value; + }; + std::string str_value; + + Type type; + + std::string toString() const; + + Type getType() const { + return type; + } + + Value(const Value& from); + + bool operator==(const Value& that) const; + bool operator!=(const Value& that) const; + + bool operator<(const Value& that) const; +}; + +/** + * Represents a log item, or a dimension item (They are essentially the same). + */ +struct FieldValue { + FieldValue() {} + FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) { + } + bool operator==(const FieldValue& that) const { + return mField == that.mField && mValue == that.mValue; + } + bool operator!=(const FieldValue& that) const { + return mField != that.mField || mValue != that.mValue; + } + bool operator<(const FieldValue& that) const { + if (mField != that.mField) { + return mField < that.mField; + } + + if (mValue != that.mValue) { + return mValue < that.mValue; + } + + return false; + } + + Field mField; + Value mValue; +}; + +bool isAttributionUidField(const FieldValue& value); + +void translateFieldMatcher(const FieldMatcher& matcher, std::vector* output); + +bool isAttributionUidField(const Field& field, const Value& value); +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index f0eaeff88d34f236033848d76bbe2a9fb424e89f..1502a00abee92f390fd685213845c17866265ebc 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -13,174 +13,262 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include #include "HashableDimensionKey.h" -#include "dimension.h" +#include "FieldValue.h" namespace android { namespace os { namespace statsd { -android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value) { - android::hash_t hash = seed; - hash = android::JenkinsHashMix(hash, android::hash_type(value.field())); - - hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case())); - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - hash = android::JenkinsHashMix( - hash, - static_cast(std::hash()(value.value_str()))); - break; - case DimensionsValue::ValueCase::kValueInt: - hash = android::JenkinsHashMix(hash, android::hash_type(value.value_int())); - break; - case DimensionsValue::ValueCase::kValueLong: - hash = android::JenkinsHashMix( - hash, android::hash_type(static_cast(value.value_long()))); - break; - case DimensionsValue::ValueCase::kValueBool: - hash = android::JenkinsHashMix(hash, android::hash_type(value.value_bool())); - break; - case DimensionsValue::ValueCase::kValueFloat: { - float floatVal = value.value_float(); - hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float)); - break; - } - case DimensionsValue::ValueCase::kValueTuple: { - hash = android::JenkinsHashMix(hash, android::hash_type( - value.value_tuple().dimensions_value_size())); - for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { - hash = android::JenkinsHashMix( - hash, - hashDimensionsValue(value.value_tuple().dimensions_value(i))); +using std::string; +using std::vector; + +android::hash_t hashDimension(const HashableDimensionKey& value) { + android::hash_t hash = 0; + for (const auto& fieldValue : value.getValues()) { + hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField())); + hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag())); + hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType())); + switch (fieldValue.mValue.getType()) { + case INT: + hash = android::JenkinsHashMix(hash, + android::hash_type(fieldValue.mValue.int_value)); + break; + case LONG: + hash = android::JenkinsHashMix(hash, + android::hash_type(fieldValue.mValue.long_value)); + break; + case STRING: + hash = android::JenkinsHashMix(hash, static_cast(std::hash()( + fieldValue.mValue.str_value))); + break; + case FLOAT: { + hash = android::JenkinsHashMix(hash, + android::hash_type(fieldValue.mValue.float_value)); + break; } - break; + default: + break; } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - break; } return JenkinsHashWhiten(hash); } -android::hash_t hashDimensionsValue(const DimensionsValue& value) { - return hashDimensionsValue(0, value); -} +// Filter fields using the matchers and output the results as a HashableDimensionKey. +// Note: HashableDimensionKey is just a wrapper for vector +bool filterValues(const vector& matcherFields, const vector& values, + vector* output) { + output->push_back(HashableDimensionKey()); + // Top level is only tag id. Now take the real child matchers + int prevAnyMatcherPrefix = 0; + size_t prevPrevFanout = 0; + size_t prevFanout = 0; -android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) { - android::hash_t hash = seed; - hash = android::JenkinsHashMix(hash, std::hash{}(dimensionKey)); - return JenkinsHashWhiten(hash); -} + // For each matcher get matched results. + vector matchedResults(2); + for (const auto& matcher : matcherFields) { + size_t num_matches = 0; + for (const auto& value : values) { + // TODO: potential optimization here to break early because all fields are naturally + // sorted. + if (value.mField.matches(matcher)) { + if (num_matches >= matchedResults.size()) { + matchedResults.resize(num_matches * 2); + } + matchedResults[num_matches].mField.setTag(value.mField.getTag()); + matchedResults[num_matches].mField.setField(value.mField.getField() & matcher.mMask); + matchedResults[num_matches].mValue = value.mValue; + num_matches++; + } + } -using std::string; + if (num_matches == 0) { + VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(), + matcher.mMatcher.getField()); + continue; + } -string HashableDimensionKey::toString() const { - return DimensionsValueToString(getDimensionsValue()); -} + if (num_matches == 1) { + for (auto& dimension : *output) { + dimension.addValue(matchedResults[0]); + } + prevAnyMatcherPrefix = 0; + prevFanout = 0; + continue; + } -bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return false; - } - if (s1.value_case() != s2.value_case()) { - return false; + // All the complexity below is because we support ANY in dimension. + bool createFanout = true; + // createFanout is true when the matcher doesn't need to follow the prev matcher's + // order. + // e.g., get (uid, tag) from any position in attribution. because we have translated + // it as 2 matchers, they need to follow the same ordering, we can't create a cross + // product of all uid and tags. + // However, if the 2 matchers have different prefix, they will create a cross product + // e.g., [any uid] [any some other repeated field], we will create a cross product for them + if (prevAnyMatcherPrefix != 0) { + int anyMatcherPrefix = 0; + bool isAnyMatcher = matcher.hasAnyPositionMatcher(&anyMatcherPrefix); + if (isAnyMatcher && anyMatcherPrefix == prevAnyMatcherPrefix) { + createFanout = false; + } else { + prevAnyMatcherPrefix = anyMatcherPrefix; + } + } + + // Each matcher should match exact one field, unless position is ANY + // When x number of fields matches a matcher, the returned dimension + // size is multiplied by x. + int oldSize; + if (createFanout) { + // First create fanout (fanout size is matchedResults.Size which could be one, + // which means we do nothing here) + oldSize = output->size(); + for (size_t i = 1; i < num_matches; i++) { + output->insert(output->end(), output->begin(), output->begin() + oldSize); + } + prevPrevFanout = oldSize; + prevFanout = num_matches; + } else { + // If we should not create fanout, e.g., uid tag from same position should be remain + // together. + oldSize = prevPrevFanout; + if (prevFanout != num_matches) { + // sanity check. + ALOGE("2 Any matcher result in different output"); + return false; + } + } + // now add the matched field value to output + for (size_t i = 0; i < num_matches; i++) { + for (int j = 0; j < oldSize; j++) { + (*output)[i * oldSize + j].addValue(matchedResults[i]); + } + } } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return (s1.value_str() == s2.value_str()); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() == s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() == s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return s1.value_bool() == s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() == s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: - { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return false; - } - bool allMatched = true; - for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { - allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - return allMatched; + + return output->size() > 0 && (*output)[0].getValues().size() > 0; +} + +void filterGaugeValues(const std::vector& matcherFields, + const std::vector& values, std::vector* output) { + for (const auto& field : matcherFields) { + for (const auto& value : values) { + if (value.mField.matches(field)) { + output->push_back(value); } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return true; + } } } -bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) { - if (s1.field() != s2.field()) { - return s1.field() < s2.field(); +void getDimensionForCondition(const LogEvent& event, Metric2Condition links, + vector* conditionDimension) { + // Get the dimension first by using dimension from what. + filterValues(links.metricFields, event.getValues(), conditionDimension); + + // Then replace the field with the dimension from condition. + for (auto& dim : *conditionDimension) { + size_t count = dim.getValues().size(); + if (count != links.conditionFields.size()) { + // ALOGE("WTF condition link is bad"); + return; + } + + for (size_t i = 0; i < count; i++) { + dim.mutableValue(i)->mField.setField(links.conditionFields[i].mMatcher.getField()); + dim.mutableValue(i)->mField.setTag(links.conditionFields[i].mMatcher.getTag()); + } } - if (s1.value_case() != s2.value_case()) { - return s1.value_case() < s2.value_case(); +} + +bool LessThan(const vector& s1, const vector& s2) { + if (s1.size() != s2.size()) { + return s1.size() < s2.size(); } - switch (s1.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return s1.value_str() < s2.value_str(); - case DimensionsValue::ValueCase::kValueInt: - return s1.value_int() < s2.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return s1.value_long() < s2.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return (int)s1.value_bool() < (int)s2.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return s1.value_float() < s2.value_float(); - case DimensionsValue::ValueCase::kValueTuple: - { - if (s1.value_tuple().dimensions_value_size() != - s2.value_tuple().dimensions_value_size()) { - return s1.value_tuple().dimensions_value_size() < - s2.value_tuple().dimensions_value_size(); - } - for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) { - if (EqualsTo(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i))) { - continue; - } else { - return LessThan(s1.value_tuple().dimensions_value(i), - s2.value_tuple().dimensions_value(i)); - } - } - return false; - } - case DimensionsValue::ValueCase::VALUE_NOT_SET: - default: - return false; + + size_t count = s1.size(); + for (size_t i = 0; i < count; i++) { + if (s1[i] != s2[i]) { + return s1[i] < s2[i]; + } } + return false; } bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { - return EqualsTo(getDimensionsValue(), that.getDimensionsValue()); + if (mValues.size() != that.getValues().size()) { + return false; + } + size_t count = mValues.size(); + for (size_t i = 0; i < count; i++) { + if (mValues[i] != (that.getValues())[i]) { + return false; + } + } + return true; }; bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { - return LessThan(getDimensionsValue(), that.getDimensionsValue()); + return LessThan(getValues(), that.getValues()); }; -string MetricDimensionKey::toString() const { - string flattened = mDimensionKeyInWhat.toString(); - flattened += mDimensionKeyInCondition.toString(); - return flattened; +bool HashableDimensionKey::contains(const HashableDimensionKey& that) const { + if (mValues.size() < that.getValues().size()) { + return false; + } + + if (mValues.size() == that.getValues().size()) { + return (*this) == that; + } + + for (const auto& value : that.getValues()) { + bool found = false; + for (const auto& myValue : mValues) { + if (value.mField == myValue.mField && value.mValue == myValue.mValue) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + + return true; +} + +string HashableDimensionKey::toString() const { + std::string output; + for (const auto& value : mValues) { + output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(), + value.mValue.toString().c_str()); + } + return output; } bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const { return mDimensionKeyInWhat == that.getDimensionKeyInWhat() && - mDimensionKeyInCondition == that.getDimensionKeyInCondition(); + mDimensionKeyInCondition == that.getDimensionKeyInCondition(); }; +string MetricDimensionKey::toString() const { + return mDimensionKeyInWhat.toString() + mDimensionKeyInCondition.toString(); +} + bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { - return toString().compare(that.toString()) < 0; -}; + if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) { + return true; + } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) { + return false; + } + return mDimensionKeyInCondition < that.getDimensionKeyInCondition(); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index a31d7a6d85c6cf74a02b7e749a4c327ed6605065..89fe317834d81b9981d4c39723d23c75b8014729 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -17,44 +17,66 @@ #pragma once #include -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include +#include "FieldValue.h" +#include "android-base/stringprintf.h" +#include "logd/LogEvent.h" namespace android { namespace os { namespace statsd { +using android::base::StringPrintf; + +struct Metric2Condition { + int64_t conditionId; + std::vector metricFields; + std::vector conditionFields; +}; + class HashableDimensionKey { public: - explicit HashableDimensionKey(const DimensionsValue& dimensionsValue) - : mDimensionsValue(dimensionsValue){}; + explicit HashableDimensionKey(const std::vector& values) { + mValues = values; + } HashableDimensionKey(){}; - HashableDimensionKey(const HashableDimensionKey& that) - : mDimensionsValue(that.getDimensionsValue()){}; - - HashableDimensionKey& operator=(const HashableDimensionKey& from) = default; + HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){}; - std::string toString() const; + inline void addValue(const FieldValue& value) { + mValues.push_back(value); + } - inline const DimensionsValue& getDimensionsValue() const { - return mDimensionsValue; + inline const std::vector& getValues() const { + return mValues; } - inline DimensionsValue* getMutableDimensionsValue() { - return &mDimensionsValue; + inline std::vector* mutableValues() { + return &mValues; } - bool operator==(const HashableDimensionKey& that) const; + inline FieldValue* mutableValue(size_t i) { + if (i >= 0 && i < mValues.size()) { + return &(mValues[i]); + } + return nullptr; + } - bool operator<(const HashableDimensionKey& that) const; + std::string toString() const; inline const char* c_str() const { return toString().c_str(); } + bool operator==(const HashableDimensionKey& that) const; + + bool operator<(const HashableDimensionKey& that) const; + + bool contains(const HashableDimensionKey& that) const; + private: - DimensionsValue mDimensionsValue; + std::vector mValues; }; class MetricDimensionKey { @@ -83,7 +105,7 @@ class MetricDimensionKey { } bool hasDimensionKeyInCondition() const { - return mDimensionKeyInCondition.getDimensionsValue().has_field(); + return mDimensionKeyInCondition.getValues().size() > 0; } bool operator==(const MetricDimensionKey& that) const; @@ -98,11 +120,32 @@ class MetricDimensionKey { HashableDimensionKey mDimensionKeyInCondition; }; -bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2); +android::hash_t hashDimension(const HashableDimensionKey& key); + +/** + * Creating HashableDimensionKeys from FieldValues using matcher. + * + * This function may make modifications to the Field if the matcher has Position=LAST or ANY in + * it. This is because: for example, when we create dimension from last uid in attribution chain, + * In one event, uid 1000 is at position 5 and it's the last + * In another event, uid 1000 is at position 6, and it's the last + * these 2 events should be mapped to the same dimension. So we will remove the original position + * from the dimension key for the uid field (by applying 0x80 bit mask). + */ +bool filterValues(const std::vector& matcherFields, const std::vector& values, + std::vector* output); + +/** + * Filter the values from FieldValues using the matchers. + * + * In contrast to the above function, this function will not do any modification to the original + * data. Considering it as taking a snapshot on the atom event. + */ +void filterGaugeValues(const std::vector& matchers, const std::vector& values, + std::vector* output); -android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value); -android::hash_t hashDimensionsValue(const DimensionsValue& value); -android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey); +void getDimensionForCondition(const LogEvent& event, Metric2Condition links, + std::vector* conditionDimension); } // namespace statsd } // namespace os @@ -116,17 +159,15 @@ using android::os::statsd::MetricDimensionKey; template <> struct hash { std::size_t operator()(const HashableDimensionKey& key) const { - return hashDimensionsValue(key.getDimensionsValue()); + return hashDimension(key); } }; template <> struct hash { std::size_t operator()(const MetricDimensionKey& key) const { - android::hash_t hash = hashDimensionsValue( - key.getDimensionKeyInWhat().getDimensionsValue()); - hash = android::JenkinsHashMix(hash, - hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue())); + android::hash_t hash = hashDimension(key.getDimensionKeyInWhat()); + hash = android::JenkinsHashMix(hash, hashDimension(key.getDimensionKeyInCondition())); return android::JenkinsHashWhiten(hash); } }; diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index a4066aa7bca55fd9bb4480ca92cd5aa767bc5417..3c9dd68c1cc29697ff8813a32f1c1c27004a7d7c 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -21,17 +21,17 @@ #include #include #include "StatsLogProcessor.h" +#include "stats_log_util.h" #include "android-base/stringprintf.h" #include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" #include "external/StatsPullerManager.h" -#include "dimension.h" -#include "field_util.h" #include "stats_util.h" #include "storage/StorageManager.h" #include #include +#include using namespace android; using android::base::StringPrintf; @@ -58,8 +58,10 @@ const int FIELD_ID_REPORTS = 2; const int FIELD_ID_UID = 1; const int FIELD_ID_ID = 2; // for ConfigMetricsReport -const int FIELD_ID_METRICS = 1; +// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp const int FIELD_ID_UID_MAP = 2; +const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3; +const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4; #define STATS_DATA_DIR "/data/misc/stats-data" @@ -71,9 +73,6 @@ StatsLogProcessor::StatsLogProcessor(const sp& uidMap, mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast), mTimeBaseSec(timeBaseSec) { - // On each initialization of StatsLogProcessor, check stats-data directory to see if there is - // any left over data to be read. - StorageManager::sendBroadcast(STATS_DATA_DIR, mSendBroadcast); StatsPullerManager statsPullerManager; statsPullerManager.SetTimeBaseSec(mTimeBaseSec); } @@ -90,27 +89,31 @@ void StatsLogProcessor::onAnomalyAlarmFired( } } +void updateUid(Value* value, int hostUid) { + int uid = value->int_value; + if (uid != hostUid) { + value->setInt(hostUid); + } +} + void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const { - std::set uidFields; if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) != android::util::kAtomsWithAttributionChain.end()) { - FieldMatcher matcher; - buildAttributionUidFieldMatcher(event->GetTagId(), Position::ANY, &matcher); - findFields(event->getFieldValueMap(), matcher, &uidFields); - } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) != - android::util::kAtomsWithUidField.end()) { - FieldMatcher matcher; - buildSimpleAtomFieldMatcher( - event->GetTagId(), 1 /* uid is always the 1st field. */, &matcher); - findFields(event->getFieldValueMap(), matcher, &uidFields); - } - - for (const auto& uidField : uidFields) { - DimensionsValue* value = event->findFieldValueOrNull(uidField); - if (value != nullptr && value->value_case() == DimensionsValue::ValueCase::kValueInt) { - const int uid = mUidMap->getHostUidOrSelf(value->value_int()); - value->set_value_int(uid); + for (auto& value : *(event->getMutableValues())) { + if (value.mField.getPosAtDepth(0) > kAttributionField) { + break; + } + if (isAttributionUidField(value)) { + const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value); + updateUid(&value.mValue, hostUid); + } } + } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) != + android::util::kAtomsWithUidField.end() && + event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) { + Value& value = (*event->getMutableValues())[0].mValue; + const int hostUid = mUidMap->getHostUidOrSelf(value.int_value); + updateUid(&value, hostUid); } } @@ -134,7 +137,7 @@ void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) { void StatsLogProcessor::OnLogEvent(LogEvent* event) { std::lock_guard lock(mMetricsMutex); StatsdStats::getInstance().noteAtomLogged( - event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC); + event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC); // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto @@ -146,6 +149,12 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { return; } + uint64_t curTimeSec = getElapsedRealtimeSec(); + if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) { + mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec); + mLastPullerCacheClearTimeSec = curTimeSec; + } + if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) { // Map the isolated uid to host uid if necessary. mapIsolatedUidToHostUidIfNecessaryLocked(event); @@ -154,13 +163,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(*event); - flushIfNecessaryLocked(event->GetTimestampNs(), pair.first, *(pair.second)); + flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second)); } } void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { std::lock_guard lock(mMetricsMutex); - ALOGD("Updated configuration for key %s", key.ToString().c_str()); + VLOG("Updated configuration for key %s", key.ToString().c_str()); sp newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap); auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) { @@ -203,27 +212,14 @@ void StatsLogProcessor::dumpStates(FILE* out, bool verbose) { } } -void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, - ConfigMetricsReportList* report) { - std::lock_guard lock(mMetricsMutex); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGW("Config source %s does not exist", key.ToString().c_str()); - return; - } - report->mutable_config_key()->set_uid(key.GetUid()); - report->mutable_config_key()->set_id(key.GetId()); - ConfigMetricsReport* configMetricsReport = report->add_reports(); - it->second->onDumpReport(dumpTimeStampNs, configMetricsReport); - // TODO: dump uid mapping. -} - -void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector* outData) { +void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs, + vector* outData) { std::lock_guard lock(mMetricsMutex); - onDumpReportLocked(key, outData); + onDumpReportLocked(key, dumpTimeStampNs, outData); } -void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector* outData) { +void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs, + vector* outData) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); @@ -247,9 +243,10 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector long long reportsToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS); + int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); // First, fill in ConfigMetricsReport using current data on memory, which // starts from filling in StatsLogReport's. - it->second->onDumpReport(&proto); + it->second->onDumpReport(dumpTimeStampNs, &proto); // Fill in UidMap. auto uidMap = mUidMap->getOutput(key); @@ -258,6 +255,12 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize); proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize); + // Fill in the timestamps. + proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS, + (long long)lastReportTimeNs); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS, + (long long)dumpTimeStampNs); + // End of ConfigMetricsReport (reports). proto.end(reportsToken); @@ -277,6 +280,7 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector iter.rp()->move(toRead); } } + StatsdStats::getInstance().noteMetricsReportSent(key); } @@ -290,6 +294,10 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { StatsdStats::getInstance().noteConfigRemoved(key); mLastBroadcastTimes.erase(key); + + if (mMetricsManagers.empty()) { + mStatsPullerManager.ForceClearPullerCache(); + } } void StatsLogProcessor::flushIfNecessaryLocked( @@ -308,7 +316,7 @@ void StatsLogProcessor::flushIfNecessaryLocked( StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data. // TODO(b/70571383): By 12/15/2017 add API to drop data directly ProtoOutputStream proto; - metricsManager.onDumpReport(&proto); + metricsManager.onDumpReport(timestampNs, &proto); StatsdStats::getInstance().noteDataDropped(key); VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) { @@ -332,10 +340,10 @@ void StatsLogProcessor::WriteDataToDisk() { for (auto& pair : mMetricsManagers) { const ConfigKey& key = pair.first; vector data; - onDumpReportLocked(key, &data); + onDumpReportLocked(key, getElapsedRealtimeNs(), &data); // TODO: Add a guardrail to prevent accumulation of file on disk. - string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, time(nullptr), - key.GetUid(), (long long)key.GetId()); + string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, + (long)getWallClockSec(), key.GetUid(), (long long)key.GetId()); StorageManager::writeFile(file_name.c_str(), &data[0], data.size()); } } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 7642aafaab437af666634a597487f6996df0c1a9..144430639d9f44cf44dbe05ab0c4108053dff643 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -21,6 +21,7 @@ #include "logd/LogReader.h" #include "metrics/MetricsManager.h" #include "packages/UidMap.h" +#include "external/StatsPullerManager.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" @@ -45,9 +46,7 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; - void onDumpReport(const ConfigKey& key, vector* outData); - void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, - ConfigMetricsReportList* report); + void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector* outData); /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */ void onAnomalyAlarmFired( @@ -75,9 +74,12 @@ private: sp mUidMap; // Reference to the UidMap to lookup app name and version for each uid. + StatsPullerManager mStatsPullerManager; + sp mAnomalyMonitor; - void onDumpReportLocked(const ConfigKey& key, vector* outData); + void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs, + vector* outData); /* Check if we should send a broadcast if approaching memory limits and if we're over, we * actually delete the data. */ @@ -96,12 +98,20 @@ private: const long mTimeBaseSec; + long mLastPullerCacheClearTimeSec = 0; + FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); - FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); - FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3); + FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1); + FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice); FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 32da94f862c59b76363ee026e78562f871f689e5..c27b130678cd141aee605ab2da2656b888d4c0dd 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "StatsService.h" +#include "stats_log_util.h" #include "android-base/stringprintf.h" #include "config/ConfigKey.h" #include "config/ConfigManager.h" @@ -77,20 +78,22 @@ StatsService::StatsService(const sp& handlerLooper) : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS)) { mUidMap = new UidMap(); + StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) { - sp sc = getStatsCompanionService(); - auto receiver = mConfigManager->GetConfigReceiver(key); - if (sc == nullptr) { - VLOG("Could not find StatsCompanionService"); - } else if (receiver.first.size() == 0) { - VLOG("Statscompanion could not find a broadcast receiver for %s", - key.ToString().c_str()); - } else { - sc->sendBroadcast(String16(receiver.first.c_str()), - String16(receiver.second.c_str())); + mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, getElapsedRealtimeSec(), + [this](const ConfigKey& key) { + sp sc = getStatsCompanionService(); + auto receiver = mConfigManager->GetConfigReceiver(key); + if (sc == nullptr) { + VLOG("Could not find StatsCompanionService"); + } else if (receiver == nullptr) { + VLOG("Statscompanion could not find a broadcast receiver for %s", + key.ToString().c_str()); + } else { + sc->sendDataBroadcast(receiver); + } } - }); + ); mConfigManager->AddListener(mProcessor); @@ -121,8 +124,6 @@ void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, */ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - status_t err; - switch (code) { case SHELL_COMMAND_TRANSACTION: { int in = data.readFileDescriptor(); @@ -237,8 +238,12 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector& return cmd_write_data_to_disk(out); } - if (!args[0].compare(String8("log-app-hook"))) { - return cmd_log_app_hook(out, args); + if (!args[0].compare(String8("log-app-breadcrumb"))) { + return cmd_log_app_breadcrumb(out, args); + } + + if (!args[0].compare(String8("clear-puller-cache"))) { + return cmd_clear_puller_cache(out); } } @@ -277,8 +282,8 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, " Flushes all data on memory to disk.\n"); fprintf(out, "\n"); fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats log-app-hook [UID] LABEL STATE\n"); - fprintf(out, " Writes an AppHook event to the statslog buffer.\n"); + fprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n"); + fprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n"); fprintf(out, " UID The uid to use. It is only possible to pass a UID\n"); fprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); fprintf(out, " uid is used.\n"); @@ -320,6 +325,10 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, "\n"); fprintf(out, "usage: adb shell cmd stats print-stats\n"); fprintf(out, " Prints some basic stats.\n"); + fprintf(out, "\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd stats clear-puller-cache\n"); + fprintf(out, " Clear cached puller data.\n"); } status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector& args) { @@ -358,12 +367,14 @@ status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector& args) { } auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, StrToInt64(name))); sp sc = getStatsCompanionService(); - if (sc != nullptr) { - sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str())); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + } else if (receiver == nullptr) { + VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str()) + } else { + sc->sendDataBroadcast(receiver); VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(), args[2].c_str()); - } else { - VLOG("Could not access statsCompanion"); } return NO_ERROR; @@ -482,7 +493,8 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector data; - mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), &data); + mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(), + &data); // TODO: print the returned StatsLogReport to file instead of printing to logcat. if (proto) { for (size_t i = 0; i < data.size(); i ++) { @@ -537,7 +549,7 @@ status_t StatsService::cmd_write_data_to_disk(FILE* out) { return NO_ERROR; } -status_t StatsService::cmd_log_app_hook(FILE* out, const Vector& args) { +status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector& args) { bool good = false; int32_t uid; int32_t label; @@ -559,13 +571,13 @@ status_t StatsService::cmd_log_app_hook(FILE* out, const Vector& args) good = true; } else { fprintf(out, - "Selecting a UID for writing AppHook can only be dumped for other UIDs on eng" - " or userdebug builds.\n"); + "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs " + "on eng or userdebug builds.\n"); } } if (good) { - fprintf(out, "Logging AppHook(%d, %d, %d) to statslog.\n", uid, label, state); - android::util::stats_write(android::util::APP_HOOK, uid, label, state); + fprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); + android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state); } else { print_cmd_help(out); return UNKNOWN_ERROR; @@ -602,9 +614,15 @@ status_t StatsService::cmd_dump_memory_info(FILE* out) { } status_t StatsService::cmd_clear_puller_cache(FILE* out) { - mStatsPullerManager.ClearPullerCache(); - fprintf(out, "Puller cached data removed!\n"); - return NO_ERROR; + IPCThreadState* ipc = IPCThreadState::self(); + VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); + if (checkCallingPermission(String16(kPermissionDump))) { + int cleared = mStatsPullerManager.ForceClearPullerCache(); + fprintf(out, "Puller removed %d cached data!\n", cleared); + return NO_ERROR; + } else { + return PERMISSION_DENIED; + } } Status StatsService::informAllUidData(const vector& uid, const vector& version, @@ -641,6 +659,7 @@ Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) { "Only system uid can call informOnePackageRemoved"); } mUidMap->removeApp(app, uid); + mConfigManager->RemoveConfigs(uid); return Status::ok(); } @@ -651,12 +670,15 @@ Status StatsService::informAnomalyAlarmFired() { return Status::fromExceptionCode(Status::EX_SECURITY, "Only system uid can call informAnomalyAlarmFired"); } - - VLOG("StatsService::informAnomalyAlarmFired succeeded"); - uint64_t currentTimeSec = time(nullptr); + uint64_t currentTimeSec = getElapsedRealtimeSec(); std::unordered_set, SpHash> anomalySet = mAnomalyMonitor->popSoonerThan(static_cast(currentTimeSec)); - mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet); + if (anomalySet.size() > 0) { + VLOG("Found an anomaly alarm that fired."); + mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet); + } else { + VLOG("Cannot find an anomaly alarm that fired. Perhaps it was recently cancelled."); + } return Status::ok(); } @@ -764,7 +786,7 @@ Status StatsService::getData(int64_t key, vector* output) { VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); if (checkCallingPermission(String16(kPermissionDump))) { ConfigKey configKey(ipc->getCallingUid(), key); - mProcessor->onDumpReport(configKey, output); + mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), output); return Status::ok(); } else { return Status::fromExceptionCode(binder::Status::EX_SECURITY); @@ -785,7 +807,6 @@ Status StatsService::getMetadata(vector* output) { Status StatsService::addConfiguration(int64_t key, const vector & config, - const String16& package, const String16& cls, bool* success) { IPCThreadState* ipc = IPCThreadState::self(); if (checkCallingPermission(String16(kPermissionDump))) { @@ -796,8 +817,33 @@ Status StatsService::addConfiguration(int64_t key, return Status::ok(); } mConfigManager->UpdateConfig(configKey, cfg); - mConfigManager->SetConfigReceiver(configKey, string(String8(package).string()), - string(String8(cls).string())); + *success = true; + return Status::ok(); + } else { + *success = false; + return Status::fromExceptionCode(binder::Status::EX_SECURITY); + } +} + +Status StatsService::removeDataFetchOperation(int64_t key, bool* success) { + IPCThreadState* ipc = IPCThreadState::self(); + if (checkCallingPermission(String16(kPermissionDump))) { + ConfigKey configKey(ipc->getCallingUid(), key); + mConfigManager->RemoveConfigReceiver(configKey); + *success = true; + return Status::ok(); + } else { + *success = false; + return Status::fromExceptionCode(binder::Status::EX_SECURITY); + } +} + +Status StatsService::setDataFetchOperation(int64_t key, const sp& intentSender, + bool* success) { + IPCThreadState* ipc = IPCThreadState::self(); + if (checkCallingPermission(String16(kPermissionDump))) { + ConfigKey configKey(ipc->getCallingUid(), key); + mConfigManager->SetConfigReceiver(configKey, intentSender); *success = true; return Status::ok(); } else { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 109752b0295fa4b34b1d21b4020f3a9bc253c033..9690de702c248c877785b34a8ef0ff681103f468 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -90,9 +90,19 @@ public: * Binder call to let clients send a configuration and indicate they're interested when they * should requestData for this configuration. */ - virtual Status addConfiguration(int64_t key, const vector & config, - const String16& package, const String16& cls, bool* success) - override; + virtual Status addConfiguration(int64_t key, const vector& config, + bool* success) override; + + /** + * Binder call to let clients register the data fetch operation for a configuration. + */ + virtual Status setDataFetchOperation(int64_t key, const sp& intentSender, + bool* success) override; + + /** + * Binder call to remove the data fetch operation for the specified config key. + */ + virtual Status removeDataFetchOperation(int64_t key, bool* success) override; /** * Binder call to allow clients to remove the specified configuration. @@ -183,9 +193,10 @@ private: status_t cmd_write_data_to_disk(FILE* out); /** - * Write an AppHook event to the StatsLog buffer, as though StatsLog.write(APP_HOOK). + * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling + * StatsLog.write(APP_BREADCRUMB_REPORTED). */ - status_t cmd_log_app_hook(FILE* out, const Vector& args); + status_t cmd_log_app_breadcrumb(FILE* out, const Vector& args); /** * Print contents of a pulled metrics source. diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp index 4912648b648d04a6faccd57d48b7562b2600fc2a..ca34dc6d87fd3e4aaa443de9ee0ad87dd26f0355 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true +#define DEBUG false #include "Log.h" #include "anomaly/AnomalyMonitor.h" @@ -36,10 +36,10 @@ void AnomalyMonitor::setStatsCompanionService(sp statsCo sp tmpForLock = mStatsCompanionService; mStatsCompanionService = statsCompanionService; if (statsCompanionService == nullptr) { - if (DEBUG) ALOGD("Erasing link to statsCompanionService"); + VLOG("Erasing link to statsCompanionService"); return; } - if (DEBUG) ALOGD("Creating link to statsCompanionService"); + VLOG("Creating link to statsCompanionService"); const sp top = mPq.top(); if (top != nullptr) { updateRegisteredAlarmTime_l(top->timestampSec); @@ -58,7 +58,7 @@ void AnomalyMonitor::add(sp alarm) { return; } // TODO: Ensure that refractory period is respected. - if (DEBUG) ALOGD("Adding alarm with time %u", alarm->timestampSec); + VLOG("Adding alarm with time %u", alarm->timestampSec); mPq.push(alarm); if (mRegisteredAlarmTimeSec < 1 || alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) { @@ -72,16 +72,16 @@ void AnomalyMonitor::remove(sp alarm) { ALOGW("Asked to remove a null alarm."); return; } - if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec); + VLOG("Removing alarm with time %u", alarm->timestampSec); bool wasPresent = mPq.remove(alarm); if (!wasPresent) return; if (mPq.empty()) { - if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); + VLOG("Queue is empty. Cancel any alarm."); cancelRegisteredAlarmTime_l(); return; } uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec; - if (DEBUG) ALOGD("Soonest alarm is %u", soonestAlarmTimeSec); + VLOG("Soonest alarm is %u", soonestAlarmTimeSec); if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) { updateRegisteredAlarmTime_l(soonestAlarmTimeSec); } @@ -91,19 +91,19 @@ void AnomalyMonitor::remove(sp alarm) { // updates to the registered alarm. unordered_set, SpHash> AnomalyMonitor::popSoonerThan( uint32_t timestampSec) { - if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec); + VLOG("Removing alarms with time <= %u", timestampSec); unordered_set, SpHash> oldAlarms; std::lock_guard lock(mLock); for (sp t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; - t = mPq.top()) { + t = mPq.top()) { oldAlarms.insert(t); mPq.pop(); // remove t } // Always update registered alarm time (if anything has changed). if (!oldAlarms.empty()) { if (mPq.empty()) { - if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); + VLOG("Queue is empty. Cancel any alarm."); cancelRegisteredAlarmTime_l(); } else { // Always update the registered alarm in this case (unlike remove()). @@ -114,7 +114,7 @@ unordered_set, SpHash> AnomalyMonitor::popS } void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { - if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec); + VLOG("Updating reg alarm time to %u", timestampSec); mRegisteredAlarmTimeSec = timestampSec; if (mStatsCompanionService != nullptr) { mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec)); @@ -123,7 +123,7 @@ void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { } void AnomalyMonitor::cancelRegisteredAlarmTime_l() { - if (DEBUG) ALOGD("Cancelling reg alarm."); + VLOG("Cancelling reg alarm."); mRegisteredAlarmTimeSec = 0; if (mStatsCompanionService != nullptr) { mStatsCompanionService->cancelAnomalyAlarm(); diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index c84a5b4509b03f6569b1c55e2660b58379543533..c40eb812f949e0aed3cec3affdc30f362d5e2f37 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -14,18 +14,15 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "AnomalyTracker.h" #include "external/Perfetto.h" #include "guardrail/StatsdStats.h" -#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" +#include "subscriber/IncidentdReporter.h" #include "subscriber/SubscriberReporter.h" -#include -#include -#include #include #include @@ -35,20 +32,17 @@ namespace statsd { // TODO: Get rid of bucketNumbers, and return to the original circular array method. AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey) - : mAlert(alert), - mConfigKey(configKey), - mNumOfPastBuckets(mAlert.num_buckets() - 1) { + : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) { VLOG("AnomalyTracker() called"); if (mAlert.num_buckets() <= 0) { - ALOGE("Cannot create AnomalyTracker with %lld buckets", - (long long)mAlert.num_buckets()); + ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets()); return; } if (!mAlert.has_trigger_if_sum_gt()) { ALOGE("Cannot create AnomalyTracker without threshold"); return; } - resetStorage(); // initialization + resetStorage(); // initialization } AnomalyTracker::~AnomalyTracker() { @@ -98,6 +92,9 @@ void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) { void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue, const int64_t& bucketNum) { + if (mNumOfPastBuckets == 0) { + return; + } flushPastBuckets(bucketNum); auto& bucket = mPastBuckets[index(bucketNum)]; @@ -112,6 +109,9 @@ void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& void AnomalyTracker::addPastBucket(std::shared_ptr bucketValues, const int64_t& bucketNum) { VLOG("addPastBucket() called."); + if (mNumOfPastBuckets == 0) { + return; + } flushPastBuckets(bucketNum); // Replace the oldest bucket with the new bucket we are adding. mPastBuckets[index(bucketNum)] = bucketValues; @@ -149,6 +149,10 @@ void AnomalyTracker::addBucketToSum(const shared_ptr& bucket) { int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const { + if (mNumOfPastBuckets == 0) { + return 0; + } + const auto& bucket = mPastBuckets[index(bucketNum)]; if (bucket == nullptr) { return 0; @@ -171,8 +175,8 @@ bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const Metric // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this. addPastBucket(key, 0, currentBucketNum - 1); } - return mAlert.has_trigger_if_sum_gt() - && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); + return mAlert.has_trigger_if_sum_gt() && + getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) { @@ -188,7 +192,7 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDim if (!mSubscriptions.empty()) { if (mAlert.has_id()) { - ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id()); + ALOGI("An anomaly (%lld) has occurred! Informing subscribers.", mAlert.id()); informSubscribers(key); } else { ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers."); @@ -229,48 +233,38 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) { VLOG("informSubscribers called."); if (mSubscriptions.empty()) { - ALOGE("Attempt to call with no subscribers."); + // The config just wanted to log the anomaly. That's fine. + VLOG("No Subscriptions were associated with the alert."); return; } - std::set incidentdSections; - for (const Subscription& subscription : mSubscriptions) { + if (subscription.probability_of_informing() < 1 + && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) { + // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always. + // The config writer was advised to use -0.1 and 1.1 for never/always. + ALOGI("Fate decided that a subscriber would not be informed."); + continue; + } switch (subscription.subscriber_information_case()) { case Subscription::SubscriberInformationCase::kIncidentdDetails: - for (int i = 0; i < subscription.incidentd_details().section_size(); i++) { - incidentdSections.insert(subscription.incidentd_details().section(i)); + if (!GenerateIncidentReport(subscription.incidentd_details(), mAlert, mConfigKey)) { + ALOGW("Failed to generate incident report."); } break; case Subscription::SubscriberInformationCase::kPerfettoDetails: - CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details()); + if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) { + ALOGW("Failed to generate prefetto traces."); + } break; case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: - SubscriberReporter::getInstance() - .alertBroadcastSubscriber(mConfigKey, subscription, key); + SubscriberReporter::getInstance().alertBroadcastSubscriber(mConfigKey, subscription, + key); break; default: break; } } - if (!incidentdSections.empty()) { - sp service = interface_cast( - defaultServiceManager()->getService(android::String16("incident"))); - if (service != NULL) { - IncidentReportArgs incidentReport; - for (const auto section : incidentdSections) { - incidentReport.addSection(section); - } - android::os::IncidentHeaderProto header; - header.set_alert_id(mAlert.id()); - header.mutable_config_key()->set_uid(mConfigKey.GetUid()); - header.mutable_config_key()->set_id(mConfigKey.GetId()); - incidentReport.addHeader(header); - service->reportIncident(incidentReport); - } else { - ALOGW("Couldn't get the incident service."); - } - } } } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index f01a97f86cf6c39ae9c1064dfb3cffb829b7b181..3be959d14109e69c138c62015b1692f548e339c7 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -16,22 +16,24 @@ #pragma once +#include // unique_ptr + +#include + #include +#include + #include "AnomalyMonitor.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include "stats_util.h" // HashableDimensionKey and DimToValMap -#include // unique_ptr -#include -#include - namespace android { namespace os { namespace statsd { -using std::unordered_map; using std::shared_ptr; +using std::unordered_map; // Does NOT allow negative values. class AnomalyTracker : public virtual RefBase { @@ -60,12 +62,11 @@ public: // Detects the alert and informs the incidentd when applicable. void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, - const MetricDimensionKey& key, - const int64_t& currentBucketValue); + const MetricDimensionKey& key, const int64_t& currentBucketValue); // Init the AnomalyMonitor which is shared across anomaly trackers. virtual void setAnomalyMonitor(const sp& anomalyMonitor) { - return; // Base AnomalyTracker class has no need for the AnomalyMonitor. + return; // Base AnomalyTracker class has no need for the AnomalyMonitor. } // Helper function to return the sum value of past buckets at given dimension. @@ -92,9 +93,10 @@ public: // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker, // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor. - virtual void informAlarmsFired(const uint64_t& timestampNs, + virtual void informAlarmsFired( + const uint64_t& timestampNs, unordered_set, SpHash>& firedAlarms) { - return; // The base AnomalyTracker class doesn't have alarms. + return; // The base AnomalyTracker class doesn't have alarms. } protected: diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp index bbee9fa5358c249adbcd740d5644dfe2e20c3e10..3ba943c310bbddf97875251f145db22065cee086 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "DurationAnomalyTracker.h" @@ -52,12 +52,12 @@ void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionK } void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey, - const uint64_t& timestampNs) { - - uint32_t timestampSec = static_cast(timestampNs / NS_PER_SEC); + const uint64_t& timestampNs) { + // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely. + uint32_t timestampSec = static_cast((timestampNs -1)/ NS_PER_SEC) + 1; // round up if (isInRefractoryPeriod(timestampNs, dimensionKey)) { - VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period"); - return; + VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period"); + timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1; } sp alarm = new AnomalyAlarm{timestampSec}; mAlarms.insert({dimensionKey, alarm}); @@ -86,15 +86,15 @@ void DurationAnomalyTracker::stopAllAlarms() { } } -void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, +void DurationAnomalyTracker::informAlarmsFired( + const uint64_t& timestampNs, unordered_set, SpHash>& firedAlarms) { - if (firedAlarms.empty() || mAlarms.empty()) return; // Find the intersection of firedAlarms and mAlarms. // The for loop is inefficient, since it loops over all keys, but that's okay since it is very // seldomly called. The alternative would be having AnomalyAlarms store information about the - // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is - // rarely ever called. + // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that + // is rarely ever called. unordered_map> matchedAlarms; for (const auto& kv : mAlarms) { if (firedAlarms.count(kv.second) > 0) { diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h index 052fdf5762892753c9873d8d5214af28173a6750..15aef29bc644c97fa4814e94f1ca7c79b42b8f9d 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h @@ -53,7 +53,8 @@ public: // and removes it from firedAlarms. // Note that this will generally be called from a different thread from the other functions; // the caller is responsible for thread safety. - void informAlarmsFired(const uint64_t& timestampNs, + void informAlarmsFired( + const uint64_t& timestampNs, unordered_set, SpHash>& firedAlarms) override; protected: @@ -71,7 +72,8 @@ protected: FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); - FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); + FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp); + FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop); }; } // namespace statsd diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto new file mode 100644 index 0000000000000000000000000000000000000000..19d00b7dc4a5bbec6598150d54c2a85e339f83de --- /dev/null +++ b/cmds/statsd/src/atom_field_options.proto @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 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. + */ + +syntax = "proto2"; + +package android.os.statsd; +option java_package = "com.android.os"; +option java_multiple_files = true; +option java_outer_classname = "AtomFieldOptions"; + +import "google/protobuf/descriptor.proto"; + +enum StateField { + // Default value for fields that are not primary or exclusive state. + STATE_FIELD_UNSET = 0; + // Fields that represent the key that the state belongs to. + PRIMARY = 1; + // The field that represents the state. It's an exclusive state. + EXCLUSIVE = 2; +} + +// Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE +// exclusive state field, and any number of primary key fields. +// For example, +// message UidProcessStateChanged { +// optional int32 uid = 1 [(stateFieldOption).option = PRIMARY]; +// optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE]; +// } +// Each of this UidProcessStateChanged atom represents a state change for a specific uid. +// A new state automatically overrides the previous state. +// +// If the atom has 2 or more primary fields, it means the combination of the primary fields are +// the primary key. +// For example: +// message ThreadStateChanged { +// optional int32 pid = 1 [(stateFieldOption).option = PRIMARY]; +// optional int32 tid = 2 [(stateFieldOption).option = PRIMARY]; +// optional int32 state = 3 [(stateFieldOption).option = EXCLUSIVE]; +// } +// +// Sometimes, there is no primary key field, when the state is GLOBAL. +// For example, +// +// message ScreenStateChanged { +// optional android.view.DisplayStateEnum state = 1 [(stateFieldOption).option = EXCLUSIVE]; +// } +// +// Only fields of primary types can be annotated. AttributionNode cannot be primary keys (and they +// usually are not). +message StateAtomFieldOption { + optional StateField option = 1 [default = STATE_FIELD_UNSET]; +} + +extend google.protobuf.FieldOptions { + // Flags to decorate an atom that presents a state change. + optional StateAtomFieldOption stateFieldOption = 50000; +} \ No newline at end of file diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index e58c53506c54e569ffa7b58042a1036d2f66450b..04ebfcd0f4dec236701c5a4e6a52e299c8f46e6f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -21,9 +21,11 @@ package android.os.statsd; option java_package = "com.android.os"; option java_outer_classname = "AtomsProto"; +import "frameworks/base/cmds/statsd/src/atom_field_options.proto"; import "frameworks/base/core/proto/android/app/enums.proto"; import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/server/enums.proto"; +import "frameworks/base/core/proto/android/telecomm/enums.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; @@ -84,7 +86,7 @@ message Atom { PacketWakeupOccurred packet_wakeup_occurred = 44; DropboxErrorChanged dropbox_error_changed = 45; AnomalyDetected anomaly_detected = 46; - AppHook app_hook = 47; + AppBreadcrumbReported app_breadcrumb_reported = 47; AppStartChanged app_start_changed = 48; AppStartCancelChanged app_start_cancel_changed = 49; AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50; @@ -98,6 +100,7 @@ message Atom { DaveyOccurred davey_occurred = 58; OverlayStateChanged overlay_state_changed = 59; ForegroundServiceStateChanged foreground_service_state_changed = 60; + CallStateChanged call_state_changed = 61; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } @@ -177,7 +180,7 @@ message AttributionNode { */ message ScreenStateChanged { // New screen state, from frameworks/base/core/proto/android/view/enums.proto. - optional android.view.DisplayStateEnum state = 1; + optional android.view.DisplayStateEnum state = 1 [(stateFieldOption).option = EXCLUSIVE]; } /** @@ -187,10 +190,10 @@ message ScreenStateChanged { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message UidProcessStateChanged { - optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + optional int32 uid = 1 [(stateFieldOption).option = PRIMARY]; // The state, from frameworks/base/core/proto/android/app/enums.proto. - optional android.app.ProcessStateEnum state = 2; + optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE]; } /** @@ -200,9 +203,10 @@ message UidProcessStateChanged { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message ProcessLifeCycleStateChanged { - optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + // TODO: should be a string tagged w/ uid annotation + optional int32 uid = 1; - // TODO: What is this? + // The process name (usually same as the app name). optional string name = 2; // What lifecycle state the process changed to. @@ -310,7 +314,7 @@ message GpsScanStateChanged { message SyncStateChanged { repeated AttributionNode attribution_node = 1; - // Name of the sync (as named in the app) + // Name of the sync (as named in the app). Can be chosen at run-time. optional string name = 2; enum State { @@ -613,7 +617,7 @@ message KernelWakeupReported { optional string wakeup_reason_name = 1; // Duration (in microseconds) for the wake-up interrupt to be serviced. - optional int64 duration_usec = 2; + optional int64 duration_micros = 2; } /** @@ -690,10 +694,10 @@ message ShutdownSequenceReported { // Beginning of shutdown time in ms using wall clock time since unix epoch. // Default: 0 if no start time received. - optional int64 start_time_ms = 3; + optional int64 start_time_millis = 3; // Duration of shutdown in ms. Default: 0 if no duration received. - optional int64 duration_ms = 4; + optional int64 duration_millis = 4; } @@ -713,18 +717,45 @@ message BootSequenceReported { optional string system_reason = 2; // End of boot time in ms from unix epoch using system wall clock. - optional int64 end_time_ms = 3; + optional int64 end_time_millis = 3; // Total boot duration in ms. - optional int64 total_duration_ms = 4; + optional int64 total_duration_millis = 4; // Bootloader duration in ms. - optional int64 bootloader_duration_ms = 5; + optional int64 bootloader_duration_millis = 5; // Time since last boot in ms. Default: 0 if not available. optional int64 time_since_last_boot = 6; } + +/** + * Logs call state and disconnect cause (if applicable). + * + * Logged from: + * packages/services/Telecomm/src/com/android/server/telecom/Call.java + */ +message CallStateChanged { + // The state of the call. Eg. DIALING, ACTIVE, ON_HOLD, DISCONNECTED. + // From frameworks/base/core/proto/android/telecomm/enums.proto. + optional android.telecom.CallStateEnum call_state = 1; + + // The reason the call disconnected. Eg. ERROR, MISSED, REJECTED, BUSY. + // This value is only applicable when the call_state is DISCONNECTED, and + // should always be UNKNOWN if the call_state is not DISCONNECTED. + // From frameworks/base/core/proto/android/telecomm/enums.proto. + optional android.telecom.DisconnectCauseEnum disconnect_cause = 2; + + // True if the call is self-managed, which are apps that use the + // telecom infrastructure to make their own calls. + optional bool self_managed = 3; + + // True if call is external. External calls are calls on connected Wear + // devices but show up in Telecom so the user can pull them onto the device. + optional bool external_call = 4; +} + /** * Logs the duration of a davey (jank of >=700ms) when it occurs * @@ -733,7 +764,7 @@ message BootSequenceReported { */ message DaveyOccurred { // Amount of time it took to render the frame. Should be >=700ms. - optional int64 jank_duration_ms = 1; + optional int64 jank_duration_millis = 1; } /** @@ -834,7 +865,7 @@ message DropboxErrorChanged { * Logged from: * frameworks/base/core/java/android/util/StatsLog.java */ -message AppHook { +message AppBreadcrumbReported { // The uid of the application that sent this custom atom. optional int32 uid = 1; @@ -895,20 +926,25 @@ message AppStartChanged { optional bool is_instant_app = 6; // Device uptime when activity started. - optional int64 activity_start_msec = 7; + optional int64 activity_start_millis = 7; optional android.app.AppTransitionReasonEnum reason = 8; - optional int32 transition_delay_msec = 9; + optional int32 transition_delay_millis = 9; // -1 if not set. - optional int32 starting_window_delay_msec = 10; + optional int32 starting_window_delay_millis = 10; // -1 if not set. - optional int32 bind_application_delay_msec = 11; - optional int32 windows_drawn_delay_msec = 12; + optional int32 bind_application_delay_millis = 11; + optional int32 windows_drawn_delay_millis = 12; // Empty if not set. optional string launch_token = 13; + // The compiler filter used when when the package was optimized. + optional string package_optimization_compilation_filter = 14; + + // The reason why the package was optimized. + optional string package_optimization_compilation_reason = 15; } message AppStartCancelChanged { @@ -952,7 +988,7 @@ message AppStartFullyDrawnChanged { optional bool transition_process_running = 5; // App startup time (until call to Activity#reportFullyDrawn()). - optional int64 app_startup_time_ms = 6; + optional int64 app_startup_time_millis = 6; } /** @@ -963,21 +999,18 @@ message AppStartFullyDrawnChanged { * frameworks/base/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java */ message PictureInPictureStateChanged { + // -1 if it is not available optional int32 uid = 1; - optional string package_name = 2; - - optional string class_name = 3; + optional string short_name = 2; - // Picture-in-Picture action occurred, similar to - // frameworks/base/proto/src/metrics_constants.proto enum State { ENTERED = 1; EXPANDED_TO_FULL_SCREEN = 2; MINIMIZED = 3; DISMISSED = 4; } - optional State state = 4; + optional State state = 3; } /** @@ -1018,6 +1051,138 @@ message ForegroundServiceStateChanged { optional State state = 3; } +/** + * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky + * behavior in its own uid. However, the metrics of these isolated uid's almost always should be + * attributed back to the parent (host) uid. One example is Chrome. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message IsolatedUidChanged { + // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid. + optional int32 parent_uid = 1; + + optional int32 isolated_uid = 2; + + // We expect an isolated uid to be removed before if it's used for another parent uid. + enum Event { + REMOVED = 0; + CREATED = 1; + } + optional Event event = 3; +} + +/* + * Logs the reception of an incoming network packet causing the main system to wake up for + * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd + * and processed by WakeupController.cpp. + */ +message PacketWakeupOccurred { + // The uid owning the socket into which the packet was delivered, or -1 if the packet was + // delivered nowhere. + optional int32 uid = 1; + // The interface name on which the packet was received. + optional string iface = 2; + // The ethertype value of the packet. + optional int32 ethertype = 3; + // String representation of the destination MAC address of the packet. + optional string destination_hardware_address = 4; + // String representation of the source address of the packet if this was an IP packet. + optional string source_ip = 5; + // String representation of the destination address of the packet if this was an IP packet. + optional string destination_ip = 6; + // The value of the protocol field if this was an IPv4 packet or the value of the Next Header + // field if this was an IPv6 packet. The range of possible values is the same for both IP + // families. + optional int32 ip_next_header = 7; + // The source port if this was a TCP or UDP packet. + optional int32 source_port = 8; + // The destination port if this was a TCP or UDP packet. + optional int32 destination_port = 9; +} + +/* + * Logs the memory stats for an app on startup. + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message AppStartMemoryStateCaptured { + // The uid if available. -1 means not available. + optional int32 uid = 1; + + // The process name. + optional string process_name = 2; + + // The activity name. + optional string activity_name = 3; + + // # of page-faults + optional int64 pgfault = 4; + + // # of major page-faults + optional int64 pgmajfault = 5; + + // RSS + optional int64 rss_in_bytes = 6; + + // CACHE + optional int64 cache_in_bytes = 7; + + // SWAP + optional int64 swap_in_bytes = 8; +} + +/* + * Logs the change in Low Memory Killer Daemon (LMKD) state which is used as start/stop boundaries + * for LMK event. + * Logged from: + * system/core/lmkd/lmkd.c + */ +message LmkStateChanged { + enum State { + UNKNOWN = 0; + START = 1; + STOP = 2; + } + optional State state = 1; +} + +/* + * Logs the event when Low Memory Killer Daemon (LMKD) kills a process to reduce memory pressure. + * Logged from: + * system/core/lmkd/lmkd.c + */ +message LmkKillOccurred { + // The uid if available. -1 means not available. + optional int32 uid = 1; + + // The process name. + optional string process_name = 2; + + // oom adj score. + optional int32 oom_score = 3; + + // # of page-faults + optional int64 pgfault = 4; + + // # of major page-faults + optional int64 pgmajfault = 5; + + // RSS + optional int64 rss_in_bytes = 6; + + // CACHE + optional int64 cache_in_bytes = 7; + + // SWAP + optional int64 swap_in_bytes = 8; +} + +////////////////////////////////////////////////////////////////////// +// Pulled atoms below this line // +////////////////////////////////////////////////////////////////////// + /** * Pulls bytes transferred via wifi (Sum of foreground and background usage). * @@ -1134,37 +1299,15 @@ message KernelWakelock { * hardware/interfaces/power/1.1/types.hal */ message SubsystemSleepState { - // Name should be in the format of XXX.YYY where XXX is subsystem name, - // YYY is corresponding voter name. - // If there are no voters, the format should just be XXX (with no dot). - // XXX and YYY should not contain a "." in it. - optional string name = 1; + // Subsystem name + optional string subsystem_name = 1; + // For PlatformLowPowerStats (hal 1.0), this is the voter name, which could be empty. + // For SubsystemLowPowerStats (hal 1.1), this is the sleep state name. + optional string subname = 2; // The number of times it entered, or voted for entering the sleep state - optional uint64 count = 2; + optional uint64 count = 3; // The length of time spent in, or spent voting for, the sleep state - optional uint64 timeMs = 3; -} - -/** - * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky - * behavior in its own uid. However, the metrics of these isolated uid's almost always should be - * attributed back to the parent (host) uid. One example is Chrome. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message IsolatedUidChanged { - // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid. - optional int32 parent_uid = 1; - - optional int32 isolated_uid = 2; - - // We expect an isolated uid to be removed before if it's used for another parent uid. - enum Event { - REMOVED = 0; - CREATED = 1; - } - optional Event event = 3; + optional uint64 time_millis = 4; } /** @@ -1177,7 +1320,7 @@ message IsolatedUidChanged { message CpuTimePerFreq { optional uint32 cluster = 1; optional uint32 freq_index = 2; - optional uint64 time_ms = 3; + optional uint64 time_millis = 3; } /** @@ -1186,8 +1329,8 @@ message CpuTimePerFreq { */ message CpuTimePerUid { optional uint64 uid = 1; - optional uint64 user_time_ms = 2; - optional uint64 sys_time_ms = 3; + optional uint64 user_time_millis = 2; + optional uint64 sys_time_millis = 3; } /** @@ -1198,36 +1341,7 @@ message CpuTimePerUid { message CpuTimePerUidFreq { optional uint64 uid = 1; optional uint64 freq_idx = 2; - optional uint64 time_ms = 3; -} - -/* - * Logs the reception of an incoming network packet causing the main system to wake up for - * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd - * and processed by WakeupController.cpp. - */ -message PacketWakeupOccurred { - // The uid owning the socket into which the packet was delivered, or -1 if the packet was - // delivered nowhere. - optional int32 uid = 1; - // The interface name on which the packet was received. - optional string iface = 2; - // The ethertype value of the packet. - optional int32 ethertype = 3; - // String representation of the destination MAC address of the packet. - optional string destination_hardware_address = 4; - // String representation of the source address of the packet if this was an IP packet. - optional string source_ip = 5; - // String representation of the destination address of the packet if this was an IP packet. - optional string destination_ip = 6; - // The value of the protocol field if this was an IPv4 packet or the value of the Next Header - // field if this was an IPv6 packet. The range of possible values is the same for both IP - // families. - optional int32 ip_next_header = 7; - // The source port if this was a TCP or UDP packet. - optional int32 source_port = 8; - // The destination port if this was a TCP or UDP packet. - optional int32 destination_port = 9; + optional uint64 time_millis = 3; } /** @@ -1235,16 +1349,16 @@ message PacketWakeupOccurred { */ message WifiActivityEnergyInfo { // timestamp(wall clock) of record creation - optional uint64 timestamp_ms = 1; + optional uint64 timestamp_millis = 1; // stack reported state // TODO: replace this with proto enum optional int32 stack_state = 2; // tx time in ms - optional uint64 controller_tx_time_ms = 3; + optional uint64 controller_tx_time_millis = 3; // rx time in ms - optional uint64 controller_rx_time_ms = 4; + optional uint64 controller_rx_time_millis = 4; // idle time in ms - optional uint64 controller_idle_time_ms = 5; + optional uint64 controller_idle_time_millis = 5; // product of current(mA), voltage(V) and time(ms) optional uint64 controller_energy_used = 6; } @@ -1254,11 +1368,11 @@ message WifiActivityEnergyInfo { */ message ModemActivityInfo { // timestamp(wall clock) of record creation - optional uint64 timestamp_ms = 1; + optional uint64 timestamp_millis = 1; // sleep time in ms. - optional uint64 sleep_time_ms = 2; + optional uint64 sleep_time_millis = 2; // idle time in ms - optional uint64 controller_idle_time_ms = 3; + optional uint64 controller_idle_time_millis = 3; /** * Tx power index * index 0 = tx_power < 0dBm @@ -1268,17 +1382,17 @@ message ModemActivityInfo { * index 4 = tx_power > 20dBm */ // tx time in ms at power level 0 - optional uint64 controller_tx_time_pl0_ms = 4; + optional uint64 controller_tx_time_pl0_millis = 4; // tx time in ms at power level 1 - optional uint64 controller_tx_time_pl1_ms = 5; + optional uint64 controller_tx_time_pl1_millis = 5; // tx time in ms at power level 2 - optional uint64 controller_tx_time_pl2_ms = 6; + optional uint64 controller_tx_time_pl2_millis = 6; // tx time in ms at power level 3 - optional uint64 controller_tx_time_pl3_ms = 7; + optional uint64 controller_tx_time_pl3_millis = 7; // tx time in ms at power level 4 - optional uint64 controller_tx_time_pl4_ms = 8; + optional uint64 controller_tx_time_pl4_millis = 8; // rx time in ms at power level 5 - optional uint64 controller_rx_time_ms = 9; + optional uint64 controller_rx_time_millis = 9; // product of current(mA), voltage(V) and time(ms) optional uint64 energy_used = 10; } @@ -1289,50 +1403,19 @@ message ModemActivityInfo { */ message BluetoothActivityInfo { // timestamp(wall clock) of record creation - optional uint64 timestamp_ms = 1; + optional uint64 timestamp_millis = 1; // bluetooth stack state optional int32 bluetooth_stack_state = 2; // tx time in ms - optional uint64 controller_tx_time_ms = 3; + optional uint64 controller_tx_time_millis = 3; // rx time in ms - optional uint64 controller_rx_time_ms = 4; + optional uint64 controller_rx_time_millis = 4; // idle time in ms - optional uint64 controller_idle_time_ms = 5; + optional uint64 controller_idle_time_millis = 5; // product of current(mA), voltage(V) and time(ms) optional uint64 energy_used = 6; } -/* - * Logs the memory stats for an app on startup. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message AppStartMemoryStateCaptured { - // The uid if available. -1 means not available. - optional int32 uid = 1; - - // The process name. - optional string process_name = 2; - - // The activity name. - optional string activity_name = 3; - - // # of page-faults - optional int64 pgfault = 4; - - // # of major page-faults - optional int64 pgmajfault = 5; - - // RSS - optional int64 rss_in_bytes = 6; - - // CACHE - optional int64 cache_in_bytes = 7; - - // SWAP - optional int64 swap_in_bytes = 8; -} - /* * Logs the memory stats for a process. */ @@ -1362,57 +1445,11 @@ message ProcessMemoryState { optional int64 swap_in_bytes = 8; } -/* - * Logs the change in Low Memory Killer Daemon (LMKD) state which is used as start/stop boundaries - * for LMK event. - * Logged from: - * system/core/lmkd/lmkd.c - */ -message LmkStateChanged { - enum State { - UNKNOWN = 0; - START = 1; - STOP = 2; - } - optional State state = 1; -} - -/* - * Logs the event when Low Memory Killer Daemon (LMKD) kills a process to reduce memory pressure. - * Logged from: - * system/core/lmkd/lmkd.c - */ -message LmkKillOccurred { - // The uid if available. -1 means not available. - optional int32 uid = 1; - - // The process name. - optional string process_name = 2; - - // oom adj score. - optional int32 oom_score = 3; - - // # of page-faults - optional int64 pgfault = 4; - - // # of major page-faults - optional int64 pgmajfault = 5; - - // RSS - optional int64 rss_in_bytes = 6; - - // CACHE - optional int64 cache_in_bytes = 7; - - // SWAP - optional int64 swap_in_bytes = 8; -} - /* * Elapsed real time from SystemClock. */ message SystemElapsedRealtime { - optional uint64 time_ms = 1; + optional uint64 time_millis = 1; } /* @@ -1423,7 +1460,7 @@ message SystemUptime { // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting // for external input). // It is not affected by clock scaling, idle, or other power saving mechanisms. - optional uint64 uptime_ms = 1; + optional uint64 uptime_millis = 1; } /* @@ -1437,8 +1474,9 @@ message SystemUptime { */ message CpuActiveTime { optional uint64 uid = 1; - optional uint64 idx = 2; - optional uint64 time_ms = 3; + optional uint32 cluster_number = 2; + optional uint64 idx = 3; + optional uint64 time_millis = 4; } /** @@ -1453,7 +1491,7 @@ message CpuActiveTime { message CpuClusterTime { optional uint64 uid = 1; optional uint64 idx = 2; - optional uint64 time_ms = 3; + optional uint64 time_millis = 3; } /* @@ -1482,5 +1520,4 @@ message RemainingBatteryCapacity { */ message FullBatteryCapacity { optional int32 capacity_uAh = 1; -} - +} \ No newline at end of file diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 4c20ccb61afe506397cd5cc4babc4ff2d77b1a70..13a2b7be9152bb3bfe74dd3eacd16cd020b1bc47 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -106,11 +106,9 @@ bool CombinationConditionTracker::init(const vector& allConditionConf } void CombinationConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, - const vector>& allConditions, - const FieldMatcher& dimensionFields, - vector& conditionCache, - std::unordered_set &dimensionsKeySet) const { + const ConditionKey& conditionParameters, const vector>& allConditions, + const std::vector& dimensionFields, vector& conditionCache, + std::unordered_set& dimensionsKeySet) const { // So far, this is fine as there is at most one child having sliced output. for (const int childIndex : mChildren) { if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { @@ -169,8 +167,8 @@ void CombinationConditionTracker::evaluateCondition( ConditionState CombinationConditionTracker::getMetConditionDimension( const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set &dimensionsKeySet) const { + const std::vector& dimensionFields, + std::unordered_set& dimensionsKeySet) const { vector conditionCache(allConditions.size(), ConditionState::kNotEvaluated); // So far, this is fine as there is at most one child having sliced output. for (const int childIndex : mChildren) { diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 0b7f9492d628dfb335628bebeed14b7138596955..ba185f6661ecc2e0f290cbd3268f01101a1081a4 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -41,17 +41,17 @@ public: std::vector& conditionCache, std::vector& changedCache) override; - void isConditionMet( - const ConditionKey& conditionParameters, - const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::vector& conditionCache, - std::unordered_set &dimensionsKeySet) const override; + void isConditionMet(const ConditionKey& conditionParameters, + const std::vector>& allConditions, + const vector& dimensionFields, + std::vector& conditionCache, + std::unordered_set& dimensionsKeySet) const override; ConditionState getMetConditionDimension( const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set &dimensionsKeySet) const override; + const vector& dimensionFields, + std::unordered_set& dimensionsKeySet) const override; + private: LogicalOperation mLogicalOperation; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 81abbdb36ee4af68f52335830f62ede8f6a18bf9..2612a9a06da1bef76b36c56b724bbe029f612e0e 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -90,14 +90,13 @@ public: virtual void isConditionMet( const ConditionKey& conditionParameters, const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::vector& conditionCache, - std::unordered_set &dimensionsKeySet) const = 0; + const vector& dimensionFields, std::vector& conditionCache, + std::unordered_set& dimensionsKeySet) const = 0; virtual ConditionState getMetConditionDimension( const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set &dimensionsKeySet) const = 0; + const vector& dimensionFields, + std::unordered_set& dimensionsKeySet) const = 0; // return the list of LogMatchingTracker index that this ConditionTracker uses. virtual const std::set& getLogTrackerIndex() const { diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index 0427700fec91f13be332f8dba69436851161cd43..c8722c362fe69802340462d14e90051153948a27 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -24,11 +24,9 @@ using std::map; using std::string; using std::vector; -ConditionState ConditionWizard::query( - const int index, const ConditionKey& parameters, - const FieldMatcher& dimensionFields, - std::unordered_set *dimensionKeySet) { - +ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters, + const vector& dimensionFields, + std::unordered_set* dimensionKeySet) { vector cache(mAllConditions.size(), ConditionState::kNotEvaluated); mAllConditions[index]->isConditionMet( @@ -37,9 +35,8 @@ ConditionState ConditionWizard::query( } ConditionState ConditionWizard::getMetConditionDimension( - const int index, const FieldMatcher& dimensionFields, - std::unordered_set *dimensionsKeySet) const { - + const int index, const vector& dimensionFields, + std::unordered_set* dimensionsKeySet) const { return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields, *dimensionsKeySet); } diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index b38b59ff4cd0bd1f00f59f600136d5a4c3fb6256..4831d5622a3db5ef594111fac098b52d30e062d7 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -39,16 +39,13 @@ public: // condition. // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case, // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. - virtual ConditionState query( - const int conditionIndex, - const ConditionKey& conditionParameters, - const FieldMatcher& dimensionFields, - std::unordered_set *dimensionKeySet); + virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters, + const vector& dimensionFields, + std::unordered_set* dimensionKeySet); virtual ConditionState getMetConditionDimension( - const int index, - const FieldMatcher& dimensionFields, - std::unordered_set *dimensionsKeySet) const; + const int index, const vector& dimensionFields, + std::unordered_set* dimensionsKeySet) const; private: std::vector> mAllConditions; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 25265d5dabd746729326f047b674630f1bda2b09..624119f3ad045f40686dd302c22465bce1ed95f9 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -19,7 +19,6 @@ #include "SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" -#include "dimension.h" #include @@ -77,10 +76,12 @@ SimpleConditionTracker::SimpleConditionTracker( mStopAllLogMatcherIndex = -1; } - mOutputDimensions = simplePredicate.dimensions(); - - if (mOutputDimensions.child_size() > 0) { - mSliced = true; + if (simplePredicate.has_dimensions()) { + translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); + if (mOutputDimensions.size() > 0) { + mSliced = true; + mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); + } } if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { @@ -104,13 +105,10 @@ bool SimpleConditionTracker::init(const vector& allConditionConfig, vector& stack) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. - if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) { - setSliced(true); - } return mInitialized; } -void print(map& conditions, const int64_t& id) { +void print(const map& conditions, const int64_t& id) { VLOG("%lld DUMP:", (long long)id); for (const auto& pair : conditions) { VLOG("\t%s : %d", pair.first.c_str(), pair.second); @@ -151,24 +149,15 @@ bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { } void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey, - bool matchStart, - std::vector& conditionCache, - std::vector& conditionChangedCache) { - if ((int)conditionChangedCache.size() <= mIndex) { - ALOGE("handleConditionEvent: param conditionChangedCache not initialized."); - return; - } - if ((int)conditionCache.size() <= mIndex) { - ALOGE("handleConditionEvent: param conditionCache not initialized."); - return; - } + bool matchStart, ConditionState* conditionCache, + bool* conditionChangedCache) { bool changed = false; auto outputIt = mSlicedConditionState.find(outputKey); ConditionState newCondition; if (hitGuardRail(outputKey)) { - conditionChangedCache[mIndex] = false; + (*conditionChangedCache) = false; // Tells the caller it's evaluated. - conditionCache[mIndex] = ConditionState::kUnknown; + (*conditionCache) = ConditionState::kUnknown; return; } if (outputIt == mSlicedConditionState.end()) { @@ -230,9 +219,8 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou print(mSlicedConditionState, mConditionId); } - conditionChangedCache[mIndex] = changed; - conditionCache[mIndex] = newCondition; - + (*conditionChangedCache) = changed; + (*conditionCache) = newCondition; VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId, conditionChangedCache[mIndex] == true); } @@ -292,42 +280,42 @@ void SimpleConditionTracker::evaluateCondition( return; } - // outputKey is the output values. e.g, uid:1234 - std::vector outputValues; - getDimensionKeys(event, mOutputDimensions, &outputValues); - if (outputValues.size() == 0) { - // The original implementation would generate an empty string dimension hash when condition - // is not sliced. - handleConditionEvent( - DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache); - } else if (outputValues.size() == 1) { - handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1, - conditionCache, conditionChangedCache); + ConditionState overallState = mInitialValue; + bool overallChanged = false; + + if (mOutputDimensions.size() == 0) { + handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState, + &overallChanged); } else { + std::vector outputValues; + filterValues(mOutputDimensions, event.getValues(), &outputValues); + // If this event has multiple nodes in the attribution chain, this log event probably will // generate multiple dimensions. If so, we will find if the condition changes for any // dimension and ask the corresponding metric producer to verify whether the actual sliced // condition has changed or not. // A high level assumption is that a predicate is either sliced or unsliced. We will never // have both sliced and unsliced version of a predicate. - for (const DimensionsValue& outputValue : outputValues) { - vector dimensionalConditionCache(conditionCache.size(), - ConditionState::kNotEvaluated); - vector dimensionalConditionChangedCache(conditionChangedCache.size(), false); - handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1, - dimensionalConditionCache, dimensionalConditionChangedCache); - OrConditionState(dimensionalConditionCache, &conditionCache); - OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache); + for (const HashableDimensionKey& outputValue : outputValues) { + // For sliced conditions, the value in the cache is not used. We don't need to update + // the overall condition state. + ConditionState tempState = ConditionState::kUnknown; + bool tempChanged = false; + handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged); + if (tempChanged) { + overallChanged = true; + } } } + conditionCache[mIndex] = overallState; + conditionChangedCache[mIndex] = overallChanged; } void SimpleConditionTracker::isConditionMet( - const ConditionKey& conditionParameters, - const vector>& allConditions, - const FieldMatcher& dimensionFields, - vector& conditionCache, - std::unordered_set &dimensionsKeySet) const { + const ConditionKey& conditionParameters, const vector>& allConditions, + const vector& dimensionFields, vector& conditionCache, + std::unordered_set& dimensionsKeySet) const { + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { // it has been evaluated. VLOG("Yes, already evaluated, %lld %d", @@ -338,8 +326,7 @@ void SimpleConditionTracker::isConditionMet( if (pair == conditionParameters.end()) { ConditionState conditionState = ConditionState::kNotEvaluated; - if (dimensionFields.has_field() && dimensionFields.child_size() > 0 && - dimensionFields.field() == mOutputDimensions.field()) { + if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) { conditionState = conditionState | getMetConditionDimension( allConditions, dimensionFields, dimensionsKeySet); } else { @@ -368,12 +355,10 @@ void SimpleConditionTracker::isConditionMet( ConditionState sliceState = startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; conditionState = conditionState | sliceState; - if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) { - HashableDimensionKey dimensionKey; - if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields, - dimensionKey.getMutableDimensionsValue())) { - dimensionsKeySet.insert(dimensionKey); - } + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + vector dimensionKeys; + filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKeys); + dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); } } else { // For unseen key, check whether the require dimensions are subset of sliced condition @@ -382,31 +367,29 @@ void SimpleConditionTracker::isConditionMet( for (const auto& slice : mSlicedConditionState) { ConditionState sliceState = slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) { + if (slice.first.contains(key)) { conditionState = conditionState | sliceState; - if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) { - HashableDimensionKey dimensionKey; - if (getSubDimension(slice.first.getDimensionsValue(), - dimensionFields, dimensionKey.getMutableDimensionsValue())) { - dimensionsKeySet.insert(dimensionKey); - } + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + vector dimensionKeys; + filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys); + + dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); + } } } } } - } conditionCache[mIndex] = conditionState; VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); } ConditionState SimpleConditionTracker::getMetConditionDimension( const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set &dimensionsKeySet) const { + const vector& dimensionFields, + std::unordered_set& dimensionsKeySet) const { ConditionState conditionState = mInitialValue; - if (!dimensionFields.has_field() || - !mOutputDimensions.has_field() || - dimensionFields.field() != mOutputDimensions.field()) { + if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 || + dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) { const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); if (itr != mSlicedConditionState.end()) { ConditionState sliceState = @@ -419,13 +402,13 @@ ConditionState SimpleConditionTracker::getMetConditionDimension( for (const auto& slice : mSlicedConditionState) { ConditionState sliceState = slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - DimensionsValue dimensionsValue; conditionState = conditionState | sliceState; - HashableDimensionKey dimensionKey; - if (sliceState == ConditionState::kTrue && - getSubDimension(slice.first.getDimensionsValue(), dimensionFields, - dimensionKey.getMutableDimensionsValue())) { - dimensionsKeySet.insert(dimensionKey); + + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + vector dimensionKeys; + filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys); + + dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); } } return conditionState; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index ce9a02d4a795357694831cace5d4817c7d72d518..c565129352973ee4a35fb9bdeacdd0c306701255 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -48,14 +48,14 @@ public: void isConditionMet(const ConditionKey& conditionParameters, const std::vector>& allConditions, - const FieldMatcher& dimensionFields, + const vector& dimensionFields, std::vector& conditionCache, - std::unordered_set &dimensionsKeySet) const override; + std::unordered_set& dimensionsKeySet) const override; ConditionState getMetConditionDimension( const std::vector>& allConditions, - const FieldMatcher& dimensionFields, - std::unordered_set &dimensionsKeySet) const override; + const vector& dimensionFields, + std::unordered_set& dimensionsKeySet) const override; private: const ConfigKey mConfigKey; @@ -73,17 +73,17 @@ private: ConditionState mInitialValue; - FieldMatcher mOutputDimensions; + std::vector mOutputDimensions; + + int mDimensionTag; std::map mSlicedConditionState; void handleStopAll(std::vector& conditionCache, std::vector& changedCache); - void handleConditionEvent(const HashableDimensionKey& outputKey, - bool matchStart, - std::vector& conditionCache, - std::vector& changedCache); + void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart, + ConditionState* conditionCache, bool* changedCache); bool hitGuardRail(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index 0ab33cfbaea11d9c890d5696ab13a49cb3fd0d76..691356b5edc62fd416a55ab96476acff7b0bebd8 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -27,7 +27,6 @@ #include "ConditionTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" -#include "dimension.h" namespace android { namespace os { @@ -97,109 +96,6 @@ ConditionState evaluateCombinationCondition(const std::vector& children, ConditionState operator|(ConditionState l, ConditionState r) { return l >= r ? l : r; } - -void OrConditionState(const std::vector& ref, vector * ored) { - if (ref.size() != ored->size()) { - return; - } - for (size_t i = 0; i < ored->size(); ++i) { - ored->at(i) = ored->at(i) | ref.at(i); - } -} - -void OrBooleanVector(const std::vector& ref, vector * ored) { - if (ref.size() != ored->size()) { - return; - } - for (size_t i = 0; i < ored->size(); ++i) { - ored->at(i) = ored->at(i) | ref.at(i); - } -} - -void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField, - std::vector *allFields) { - if (matcher.has_position()) { - leafField->set_position_index(0); - } - if (matcher.child_size() == 0) { - allFields->push_back(*rootField); - return; - } - for (int i = 0; i < matcher.child_size(); ++i) { - Field* newLeafField = leafField->add_child(); - newLeafField->set_field(matcher.child(i).field()); - getFieldsFromFieldMatcher(matcher.child(i), rootField, newLeafField, allFields); - } -} - -void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector *allFields) { - if (!matcher.has_field()) { - return; - } - Field rootField; - rootField.set_field(matcher.field()); - getFieldsFromFieldMatcher(matcher, &rootField, &rootField, allFields); -} - -void flattenValueLeaves(const DimensionsValue& value, - std::vector *allLaves) { - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::kValueInt: - case DimensionsValue::ValueCase::kValueLong: - case DimensionsValue::ValueCase::kValueBool: - case DimensionsValue::ValueCase::kValueFloat: - case DimensionsValue::ValueCase::VALUE_NOT_SET: - allLaves->push_back(&value); - break; - case DimensionsValue::ValueCase::kValueTuple: - for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { - flattenValueLeaves(value.value_tuple().dimensions_value(i), allLaves); - } - break; - } -} - -void getDimensionKeysForCondition( - const LogEvent& event, const MetricConditionLink& link, - std::vector *hashableDimensionKeys) { - std::vector whatFields; - getFieldsFromFieldMatcher(link.fields_in_what(), &whatFields); - std::vector conditionFields; - getFieldsFromFieldMatcher(link.fields_in_condition(), &conditionFields); - - // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and - // directly construct the full condition value tree. - std::vector whatValues; - getDimensionKeys(event, link.fields_in_what(), &whatValues); - - for (size_t i = 0; i < whatValues.size(); ++i) { - std::vector whatLeaves; - flattenValueLeaves(whatValues[i], &whatLeaves); - if (whatLeaves.size() != whatFields.size() || - whatLeaves.size() != conditionFields.size()) { - ALOGE("Dimensions between what and condition not equal."); - return; - } - FieldValueMap conditionValueMap; - for (size_t j = 0; j < whatLeaves.size(); ++j) { - DimensionsValue* conditionValue = &conditionValueMap[conditionFields[j]]; - *conditionValue = *whatLeaves[i]; - if (!setFieldInLeafValueProto(conditionFields[j], conditionValue)) { - ALOGE("Not able to reset the field for condition leaf value."); - return; - } - } - std::vector conditionValueTrees; - findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValueTrees); - if (conditionValueTrees.size() != 1) { - ALOGE("Not able to find unambiguous field value in condition atom."); - continue; - } - hashableDimensionKeys->push_back(HashableDimensionKey(conditionValueTrees[0])); - } -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h index a7288beb69ca41b6f8411de86d0e9c2031bfba0e..fed90ec3da37fedfcaa2231c317545bc3ad51fd3 100644 --- a/cmds/statsd/src/condition/condition_util.h +++ b/cmds/statsd/src/condition/condition_util.h @@ -33,16 +33,10 @@ enum ConditionState { }; ConditionState operator|(ConditionState l, ConditionState r); -void OrConditionState(const std::vector& ref, vector * ored); -void OrBooleanVector(const std::vector& ref, vector * ored); ConditionState evaluateCombinationCondition(const std::vector& children, const LogicalOperation& operation, const std::vector& conditionCache); - -void getDimensionKeysForCondition( - const LogEvent& event, const MetricConditionLink& link, - std::vector *dimensionKeys); } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 61eeee3796df0fb3a42e3ff4a859bd54fe3124b4..d0f55abe7033476fe09633d3405cab14f6ac5bc8 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -59,71 +59,106 @@ void ConfigManager::StartupForTest() { } void ConfigManager::AddListener(const sp& listener) { + lock_guard lock(mMutex); mListeners.push_back(listener); } void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) { - // Add to set - mConfigs.insert(key); + vector> broadcastList; + { + lock_guard lock(mMutex); - // Save to disk - update_saved_configs(key, config); + // Add to set + mConfigs.insert(key); + + // Save to disk + update_saved_configs_locked(key, config); + + for (sp listener : mListeners) { + broadcastList.push_back(listener); + } + } // Tell everyone - for (auto& listener : mListeners) { + for (sp listener:broadcastList) { listener->OnConfigUpdated(key, config); } } -void ConfigManager::SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls) { - mConfigReceivers[key] = pair(pkg, cls); +void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp& intentSender) { + lock_guard lock(mMutex); + mConfigReceivers[key] = intentSender; } void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { + lock_guard lock(mMutex); mConfigReceivers.erase(key); } void ConfigManager::RemoveConfig(const ConfigKey& key) { - auto it = mConfigs.find(key); - if (it != mConfigs.end()) { - // Remove from map - mConfigs.erase(it); + vector> broadcastList; + { + lock_guard lock(mMutex); + + auto it = mConfigs.find(key); + if (it != mConfigs.end()) { + // Remove from map + mConfigs.erase(it); + + for (sp listener : mListeners) { + broadcastList.push_back(listener); + } + } - // Tell everyone - for (auto& listener : mListeners) { - listener->OnConfigRemoved(key); + auto itReceiver = mConfigReceivers.find(key); + if (itReceiver != mConfigReceivers.end()) { + // Remove from map + mConfigReceivers.erase(itReceiver); } + + // Remove from disk. There can still be a lingering file on disk so we check + // whether or not the config was on memory. + remove_saved_configs(key); } - // Remove from disk. There can still be a lingering file on disk so we check - // whether or not the config was on memory. - remove_saved_configs(key); + for (sp listener:broadcastList) { + listener->OnConfigRemoved(key); + } } void ConfigManager::remove_saved_configs(const ConfigKey& key) { - string suffix = StringPrintf("%d-%lld", key.GetUid(), (long long)key.GetId()); + string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()); StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str()); } void ConfigManager::RemoveConfigs(int uid) { vector removed; + vector> broadcastList; + { + lock_guard lock(mMutex); + + + for (auto it = mConfigs.begin(); it != mConfigs.end();) { + // Remove from map + if (it->GetUid() == uid) { + remove_saved_configs(*it); + removed.push_back(*it); + mConfigReceivers.erase(*it); + it = mConfigs.erase(it); + } else { + it++; + } + } - for (auto it = mConfigs.begin(); it != mConfigs.end();) { - // Remove from map - if (it->GetUid() == uid) { - remove_saved_configs(*it); - removed.push_back(*it); - mConfigReceivers.erase(*it); - it = mConfigs.erase(it); - } else { - it++; + for (sp listener : mListeners) { + broadcastList.push_back(listener); } } // Remove separately so if they do anything in the callback they can't mess up our iteration. for (auto& key : removed) { // Tell everyone - for (auto& listener : mListeners) { + for (sp listener:broadcastList) { listener->OnConfigRemoved(key); } } @@ -131,27 +166,38 @@ void ConfigManager::RemoveConfigs(int uid) { void ConfigManager::RemoveAllConfigs() { vector removed; + vector> broadcastList; + { + lock_guard lock(mMutex); - for (auto it = mConfigs.begin(); it != mConfigs.end();) { - // Remove from map - removed.push_back(*it); - auto receiverIt = mConfigReceivers.find(*it); - if (receiverIt != mConfigReceivers.end()) { - mConfigReceivers.erase(*it); + + for (auto it = mConfigs.begin(); it != mConfigs.end();) { + // Remove from map + removed.push_back(*it); + auto receiverIt = mConfigReceivers.find(*it); + if (receiverIt != mConfigReceivers.end()) { + mConfigReceivers.erase(*it); + } + it = mConfigs.erase(it); + } + + for (sp listener : mListeners) { + broadcastList.push_back(listener); } - it = mConfigs.erase(it); } // Remove separately so if they do anything in the callback they can't mess up our iteration. for (auto& key : removed) { // Tell everyone - for (auto& listener : mListeners) { + for (sp listener:broadcastList) { listener->OnConfigRemoved(key); } } } vector ConfigManager::GetAllConfigKeys() const { + lock_guard lock(mMutex); + vector ret; for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) { ret.push_back(*it); @@ -159,29 +205,32 @@ vector ConfigManager::GetAllConfigKeys() const { return ret; } -const pair ConfigManager::GetConfigReceiver(const ConfigKey& key) const { +const sp ConfigManager::GetConfigReceiver(const ConfigKey& key) const { + lock_guard lock(mMutex); + auto it = mConfigReceivers.find(key); if (it == mConfigReceivers.end()) { - return pair(); + return nullptr; } else { return it->second; } } void ConfigManager::Dump(FILE* out) { + lock_guard lock(mMutex); + fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size()); fprintf(out, " uid name\n"); for (const auto& key : mConfigs) { fprintf(out, " %6d %lld\n", key.GetUid(), (long long)key.GetId()); auto receiverIt = mConfigReceivers.find(key); if (receiverIt != mConfigReceivers.end()) { - fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(), - receiverIt->second.second.c_str()); + fprintf(out, " -> received by PendingIntent as binder\n"); } } } -void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) { +void ConfigManager::update_saved_configs_locked(const ConfigKey& key, const StatsdConfig& config) { // If there is a pre-existing config with same key we should first delete it. remove_saved_configs(key); diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h index ad666bc15b848859982a138c99854f686c6e0dc7..a0c1c1cb16f8c1e8da16793eff00c0c4f093c558 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -16,10 +16,12 @@ #pragma once +#include "binder/IBinder.h" #include "config/ConfigKey.h" #include "config/ConfigListener.h" #include +#include #include #include @@ -68,12 +70,12 @@ public: /** * Sets the broadcast receiver for a configuration key. */ - void SetConfigReceiver(const ConfigKey& key, const std::string& pkg, const std::string& cls); + void SetConfigReceiver(const ConfigKey& key, const sp& intentSender); /** * Returns the package name and class name representing the broadcast receiver for this config. */ - const std::pair GetConfigReceiver(const ConfigKey& key) const; + const sp GetConfigReceiver(const ConfigKey& key) const; /** * Returns all config keys registered. @@ -108,10 +110,12 @@ public: void Dump(FILE* out); private: + mutable std::mutex mMutex; + /** * Save the configs to disk. */ - void update_saved_configs(const ConfigKey& key, const StatsdConfig& config); + void update_saved_configs_locked(const ConfigKey& key, const StatsdConfig& config); /** * Remove saved configs from disk. @@ -124,10 +128,10 @@ private: std::set mConfigs; /** - * Each config key can be subscribed by up to one receiver, specified as the package name and - * class name. + * Each config key can be subscribed by up to one receiver, specified as IBinder from + * PendingIntent. */ - std::map> mConfigReceivers; + std::map> mConfigReceivers; /** * The ConfigListeners that will be told about changes. diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp deleted file mode 100644 index 8a2e87128319cc250ee807a866226912ff0e5931..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/dimension.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#include "Log.h" - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h" -#include "dimension.h" - - -namespace android { -namespace os { -namespace statsd { - -const DimensionsValue* getSingleLeafValue(const DimensionsValue* value) { - if (value->value_case() == DimensionsValue::ValueCase::kValueTuple) { - return getSingleLeafValue(&value->value_tuple().dimensions_value(0)); - } else { - return value; - } -} - -DimensionsValue getSingleLeafValue(const DimensionsValue& value) { - const DimensionsValue* leafValue = getSingleLeafValue(&value); - return *leafValue; -} - -void appendLeafNodeToTree(const Field& field, - const DimensionsValue& value, - DimensionsValue* parentValue) { - if (field.child_size() <= 0) { - *parentValue = value; - parentValue->set_field(field.field()); - return; - } - parentValue->set_field(field.field()); - int idx = -1; - for (int i = 0; i < parentValue->mutable_value_tuple()->dimensions_value_size(); ++i) { - if (parentValue->mutable_value_tuple()->dimensions_value(i).field() == - field.child(0).field()) { - idx = i; - } - } - if (idx < 0) { - parentValue->mutable_value_tuple()->add_dimensions_value(); - idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1; - } - appendLeafNodeToTree( - field.child(0), value, - parentValue->mutable_value_tuple()->mutable_dimensions_value(idx)); -} - -void appendLeafNodeToTrees(const Field& field, - const DimensionsValue& node, - std::vector* rootTrees) { - if (rootTrees == nullptr) { - return; - } - if (rootTrees->empty()) { - DimensionsValue tree; - appendLeafNodeToTree(field, node, &tree); - rootTrees->push_back(tree); - } else { - for (size_t i = 0; i < rootTrees->size(); ++i) { - appendLeafNodeToTree(field, node, &rootTrees->at(i)); - } - } -} - -namespace { - -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector* rootDimensionsValues); - -void findNonRepeatedDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector* rootValues) { - if (matcher.child_size() > 0) { - Field* newLeafField = leafField->add_child(); - for (const auto& childMatcher : matcher.child()) { - newLeafField->set_field(childMatcher.field()); - findDimensionsValues(fieldValueMap, childMatcher, rootField, newLeafField, rootValues); - } - leafField->clear_child(); - } else { - auto ret = fieldValueMap.equal_range(*rootField); - int found = 0; - for (auto it = ret.first; it != ret.second; ++it) { - found++; - } - // Not found. - if (found <= 0) { - return; - } - if (found > 1) { - ALOGE("Found multiple values for optional field."); - return; - } - appendLeafNodeToTrees(*rootField, ret.first->second, rootValues); - } -} - -void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector* rootValues) { - if (matcher.position() == Position::FIRST) { - leafField->set_position_index(0); - findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, rootValues); - leafField->clear_position_index(); - } else { - auto itLower = fieldValueMap.lower_bound(*rootField); - if (itLower == fieldValueMap.end()) { - return; - } - const int leafFieldNum = leafField->field(); - leafField->set_field(leafFieldNum + 1); - auto itUpper = fieldValueMap.lower_bound(*rootField); - // Resets the field number. - leafField->set_field(leafFieldNum); - - switch (matcher.position()) { - case Position::LAST: - { - itUpper--; - if (itUpper != fieldValueMap.end()) { - int last_index = getPositionByReferenceField(*rootField, itUpper->first); - if (last_index < 0) { - return; - } - leafField->set_position_index(last_index); - findNonRepeatedDimensionsValues( - fieldValueMap, matcher, rootField, leafField, rootValues); - leafField->clear_position_index(); - } - } - break; - case Position::ANY: - { - std::set indexes; - for (auto it = itLower; it != itUpper; ++it) { - int index = getPositionByReferenceField(*rootField, it->first); - if (index >= 0) { - indexes.insert(index); - } - } - if (!indexes.empty()) { - std::vector allValues; - for (const int index : indexes) { - leafField->set_position_index(index); - std::vector newValues = *rootValues; - findNonRepeatedDimensionsValues( - fieldValueMap, matcher, rootField, leafField, &newValues); - allValues.insert(allValues.end(), newValues.begin(), newValues.end()); - leafField->clear_position_index(); - } - rootValues->clear(); - rootValues->insert(rootValues->end(), allValues.begin(), allValues.end()); - } - } - break; - default: - break; - } - } -} - -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::vector* rootDimensionsValues) { - if (!matcher.has_position()) { - findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, - rootDimensionsValues); - } else { - findRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, - rootDimensionsValues); - } -} - -} // namespace - -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - std::vector* rootDimensionsValues) { - Field rootField; - buildSimpleAtomField(matcher.field(), &rootField); - findDimensionsValues(fieldValueMap, matcher, &rootField, &rootField, rootDimensionsValues); -} - -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) { - matcher->set_field(tagId); -} - -void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) { - matcher->set_field(tagId); - matcher->add_child()->set_field(fieldNum); -} - -constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1; -constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1; -constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2; - -void buildAttributionUidFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher) { - matcher->set_field(tagId); - auto child = matcher->add_child(); - child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); - child->set_position(position); - child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); -} - -void buildAttributionTagFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher) { - matcher->set_field(tagId); - FieldMatcher* child = matcher->add_child(); - child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); - child->set_position(position); - child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); -} - -void buildAttributionFieldMatcher(const int tagId, const Position position, FieldMatcher* matcher) { - matcher->set_field(tagId); - FieldMatcher* child = matcher->add_child(); - child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); - child->set_position(position); - child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); - child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); -} - -void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) { - if (!value.has_field()) { - return; - } - *flattened += std::to_string(value.field()); - *flattened += ":"; - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - *flattened += value.value_str(); - break; - case DimensionsValue::ValueCase::kValueInt: - *flattened += std::to_string(value.value_int()); - break; - case DimensionsValue::ValueCase::kValueLong: - *flattened += std::to_string(value.value_long()); - break; - case DimensionsValue::ValueCase::kValueBool: - *flattened += std::to_string(value.value_bool()); - break; - case DimensionsValue::ValueCase::kValueFloat: - *flattened += std::to_string(value.value_float()); - break; - case DimensionsValue::ValueCase::kValueTuple: - { - *flattened += "{"; - for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { - DimensionsValueToString(value.value_tuple().dimensions_value(i), flattened); - *flattened += "|"; - } - *flattened += "}"; - } - break; - case DimensionsValue::ValueCase::VALUE_NOT_SET: - break; - } -} - -std::string DimensionsValueToString(const DimensionsValue& value) { - std::string flatten; - DimensionsValueToString(value, &flatten); - return flatten; -} - -bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub) { - if (dimension.field() != sub.field()) { - return false; - } - if (dimension.value_case() != sub.value_case()) { - return false; - } - switch (dimension.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return dimension.value_str() == sub.value_str(); - case DimensionsValue::ValueCase::kValueInt: - return dimension.value_int() == sub.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return dimension.value_long() == sub.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return dimension.value_bool() == sub.value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return dimension.value_float() == sub.value_float(); - case DimensionsValue::ValueCase::kValueTuple: { - if (dimension.value_tuple().dimensions_value_size() < - sub.value_tuple().dimensions_value_size()) { - return false; - } - bool allSub = true; - for (int i = 0; allSub && i < sub.value_tuple().dimensions_value_size(); ++i) { - bool isSub = false; - for (int j = 0; !isSub && - j < dimension.value_tuple().dimensions_value_size(); ++j) { - isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j), - sub.value_tuple().dimensions_value(i)); - } - allSub &= isSub; - } - return allSub; - } - break; - case DimensionsValue::ValueCase::VALUE_NOT_SET: - return false; - default: - return false; - } -} - -long getLongFromDimenValue(const DimensionsValue& dimensionValue) { - switch (dimensionValue.value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return dimensionValue.value_int(); - case DimensionsValue::ValueCase::kValueLong: - return dimensionValue.value_long(); - case DimensionsValue::ValueCase::kValueBool: - return dimensionValue.value_bool() ? 1 : 0; - case DimensionsValue::ValueCase::kValueFloat: - return (int64_t)dimensionValue.value_float(); - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: - return 0; - } -} - -bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher, - DimensionsValue* subDimension) { - if (!matcher.has_field()) { - return false; - } - if (matcher.field() != dimension.field()) { - return false; - } - if (matcher.child_size() <= 0) { - if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple || - dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) { - return false; - } - *subDimension = dimension; - return true; - } else { - if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) { - return false; - } - bool found_value = true; - auto value_tuple = dimension.value_tuple(); - subDimension->set_field(dimension.field()); - for (int i = 0; found_value && i < matcher.child_size(); ++i) { - int j = 0; - for (; j < value_tuple.dimensions_value_size(); ++j) { - if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) { - break; - } - } - if (j < value_tuple.dimensions_value_size()) { - found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i), - subDimension->mutable_value_tuple()->add_dimensions_value()); - } else { - found_value = false; - } - } - return found_value; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h deleted file mode 100644 index 138c6e9b0160c3fd2642f1af149f38ea6d18e56d..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/dimension.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include -#include -#include -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "field_util.h" - -namespace android { -namespace os { -namespace statsd { - -// Returns the leaf node from the DimensionsValue proto. It assume that the input has only one -// leaf node at most. -const DimensionsValue* getSingleLeafValue(const DimensionsValue* value); -DimensionsValue getSingleLeafValue(const DimensionsValue& value); - -// Appends the leaf node to the parent tree. -void appendLeafNodeToTree(const Field& field, const DimensionsValue& value, DimensionsValue* tree); - -// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto -// represents a tree. When the input proto has repeated fields and the input "dimensions" wants -// "ANY" locations, it will return multiple trees. -void findDimensionsValues( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - std::vector* rootDimensionsValues); - -// Utils to build FieldMatcher proto for simple one-depth atoms. -void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher); -void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher); - -// Utils to build FieldMatcher proto for attribution nodes. -void buildAttributionUidFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher); -void buildAttributionTagFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher); -void buildAttributionFieldMatcher(const int tagId, const Position position, - FieldMatcher* matcher); - -// Utils to print pretty string for DimensionsValue proto. -std::string DimensionsValueToString(const DimensionsValue& value); -void DimensionsValueToString(const DimensionsValue& value, std::string *flattened); - -bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub); - -// Helper function to get long value from the DimensionsValue proto. -long getLongFromDimenValue(const DimensionsValue& dimensionValue); - -bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher, - DimensionsValue* subDimension); -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp index a751273247456a4bf113b7de9b9cab7b980631fa..d1d9d3778267faefd7f5ab657cacccf6bcd2159c 100644 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -25,6 +25,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -62,7 +63,9 @@ bool CpuTimePerUidFreqPuller::PullInternal(vector>* data) { return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); + char buf[kLineBufferSize]; // first line prints the format and frequencies fin.getline(buf, kLineBufferSize); @@ -77,7 +80,8 @@ bool CpuTimePerUidFreqPuller::PullInternal(vector>* data) { int idx = 0; do { timeMs = std::stoull(pch); - auto ptr = make_shared(android::util::CPU_TIME_PER_UID_FREQ, timestamp); + auto ptr = make_shared(android::util::CPU_TIME_PER_UID_FREQ, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(idx); ptr->write(timeMs); diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp index e7ea4b9abf0cdcca4e37f9325c185f5ca2d98331..568b8f0c0c535171c391b2da04a732d15f5e95df 100644 --- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -24,6 +24,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -57,7 +58,8 @@ bool CpuTimePerUidPuller::PullInternal(vector>* data) { return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); char buf[kLineBufferSize]; char* pch; while (!fin.eof()) { @@ -70,7 +72,8 @@ bool CpuTimePerUidPuller::PullInternal(vector>* data) { pch = strtok(buf, " "); uint64_t sysTimeMs = std::stoull(pch); - auto ptr = make_shared(android::util::CPU_TIME_PER_UID, timestamp); + auto ptr = make_shared(android::util::CPU_TIME_PER_UID, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(userTimeMs); ptr->write(sysTimeMs); diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp index 7a2d1991a5389caa7d1c6aa7efef4ca090f8a310..0b545ccb3658762b0e05f6b651150dcef4594d20 100644 --- a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp +++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -23,6 +23,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -57,7 +58,9 @@ bool KernelUidCpuActiveTimeReader::PullInternal(vector>* da return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); + char buf[kLineBufferSize]; char* pch; while (!fin.eof()) { @@ -70,7 +73,7 @@ bool KernelUidCpuActiveTimeReader::PullInternal(vector>* da int idx = 0; do { timeMs = std::stoull(pch); - auto ptr = make_shared(mTagId, timestamp); + auto ptr = make_shared(mTagId, wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(idx); ptr->write(timeMs); diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp index 7426e743c40f8af5aee59549bf9214bfe9827475..cc80204b105ebe981ab577e21cce10430fa866c8 100644 --- a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp +++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -22,6 +22,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -56,7 +57,8 @@ bool KernelUidCpuClusterTimeReader::PullInternal(vector>* d return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); char buf[kLineBufferSize]; char* pch; while (!fin.eof()) { @@ -69,7 +71,7 @@ bool KernelUidCpuClusterTimeReader::PullInternal(vector>* d int idx = 0; do { timeMs = std::stoull(pch); - auto ptr = make_shared(mTagId, timestamp); + auto ptr = make_shared(mTagId, wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(idx); ptr->write(timeMs); diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp index 1d8a96850c4be8721705355c4468ff5f6f52e6e3..b09d373a5f3bfdc49b433bb4c5b24638fdbae707 100644 --- a/cmds/statsd/src/external/Perfetto.cpp +++ b/cmds/statsd/src/external/Perfetto.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert @@ -36,7 +37,7 @@ namespace os { namespace statsd { bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) { - ALOGD("Starting trace collection through perfetto"); + VLOG("Starting trace collection through perfetto"); if (!config.has_trace_config()) { ALOGE("The perfetto trace config is empty, aborting"); @@ -118,7 +119,7 @@ bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) { return false; } - ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded"); + VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded"); return true; } diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp index 72fb5ffd4b90f71ce92f8768fc729a74be9a6519..261cb4332dd60604ae585dda8ff9718c12bbcbe2 100644 --- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp @@ -25,6 +25,7 @@ #include "ResourceHealthManagerPuller.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using android::hardware::hidl_vec; using android::hardware::health::V2_0::get_health_service; @@ -61,7 +62,8 @@ bool ResourceHealthManagerPuller::PullInternal(vector>* dat return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); data->clear(); bool result_success = true; @@ -72,12 +74,14 @@ bool ResourceHealthManagerPuller::PullInternal(vector>* dat return; } if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) { - auto ptr = make_shared(android::util::REMAINING_BATTERY_CAPACITY, timestamp); + auto ptr = make_shared(android::util::REMAINING_BATTERY_CAPACITY, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(v.legacy.batteryChargeCounter); ptr->init(); data->push_back(ptr); } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) { - auto ptr = make_shared(android::util::FULL_BATTERY_CAPACITY, timestamp); + auto ptr = make_shared(android::util::FULL_BATTERY_CAPACITY, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(v.legacy.batteryFullCharge); ptr->init(); data->push_back(ptr); diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp index b955f1cdaf5a4f070c2069a201978ae049fd4a53..bd859fd79a09f50a4ea81f062823706f8d63362d 100644 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp +++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true +#define DEBUG false #include "Log.h" #include @@ -22,6 +22,7 @@ #include #include "StatsCompanionServicePuller.h" #include "StatsService.h" +#include "stats_log_util.h" #include "guardrail/StatsdStats.h" using namespace android; @@ -53,18 +54,18 @@ bool StatsCompanionServicePuller::PullInternal(vector >* da return false; } data->clear(); - int timestamp = time(nullptr); + int32_t timestampSec = getWallClockSec(); for (const StatsLogEventWrapper& it : returned_value) { log_msg tmp; tmp.entry_v1.len = it.bytes.size(); // Manually set the header size to 28 bytes to match the pushed log events. tmp.entry.hdr_size = kLogMsgHeaderSize; - tmp.entry_v1.sec = timestamp; + tmp.entry_v1.sec = timestampSec; // And set the received bytes starting after the 28 bytes reserved for header. std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize); data->push_back(make_shared(tmp)); } - ALOGD("StatsCompanionServicePuller::pull succeeded for %d", mTagId); + VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId); return true; } else { ALOGW("statsCompanion not found!"); diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index da14434737afbf543089f25c478ae92c185c9b38..9513cc521af71239a04ca6ecebfa3c47eb3190f2 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -19,6 +19,8 @@ #include "StatsPuller.h" #include "guardrail/StatsdStats.h" +#include "puller_util.h" +#include "stats_log_util.h" namespace android { namespace os { @@ -26,6 +28,9 @@ namespace statsd { using std::lock_guard; +sp StatsPuller::mUidMap = nullptr; +void StatsPuller::SetUidMap(const sp& uidMap) { mUidMap = uidMap; } + // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently StatsPuller::StatsPuller(const int tagId) : mTagId(tagId) { @@ -40,7 +45,7 @@ StatsPuller::StatsPuller(const int tagId) bool StatsPuller::Pull(std::vector>* data) { lock_guard lock(mLock); StatsdStats::getInstance().notePull(mTagId); - long curTime = time(nullptr); + long curTime = getElapsedRealtimeSec(); if (curTime - mLastPullTimeSec < mCoolDownSec) { (*data) = mCachedData; StatsdStats::getInstance().notePullFromCache(mTagId); @@ -54,14 +59,30 @@ bool StatsPuller::Pull(std::vector>* data) { mLastPullTimeSec = curTime; bool ret = PullInternal(&mCachedData); if (ret) { - (*data) = mCachedData; + mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId); + (*data) = mCachedData; } return ret; } -void StatsPuller::ClearCache() { +int StatsPuller::ForceClearCache() { + return clearCache(); +} + +int StatsPuller::clearCache() { lock_guard lock(mLock); + int ret = mCachedData.size(); mCachedData.clear(); + mLastPullTimeSec = 0; + return ret; +} + +int StatsPuller::ClearCacheIfNecessary(long timestampSec) { + if (timestampSec - mLastPullTimeSec > mCoolDownSec) { + return clearCache(); + } else { + return 0; + } } } // namespace statsd diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h index bc7c45f535d1f810cf041b3e05db0c58c28c3f14..82a8611b295faac70d057df6cab5c974521c0694 100644 --- a/cmds/statsd/src/external/StatsPuller.h +++ b/cmds/statsd/src/external/StatsPuller.h @@ -18,11 +18,14 @@ #include #include +#include #include #include +#include "packages/UidMap.h" -#include "logd/LogEvent.h" #include "guardrail/StatsdStats.h" +#include "logd/LogEvent.h" +#include "puller_util.h" using android::os::StatsLogEventWrapper; @@ -30,7 +33,7 @@ namespace android { namespace os { namespace statsd { -class StatsPuller { +class StatsPuller : public virtual RefBase { public: StatsPuller(const int tagId); @@ -38,9 +41,15 @@ public: bool Pull(std::vector>* data); - void ClearCache(); + // Clear cache immediately + int ForceClearCache(); + + // Clear cache if elapsed time is more than cooldown time + int ClearCacheIfNecessary(long timestampSec); -protected: + static void SetUidMap(const sp& uidMap); + + protected: // The atom tag id this puller pulls const int mTagId; @@ -61,6 +70,10 @@ private: std::vector> mCachedData; long mLastPullTimeSec; + + int clearCache(); + + static sp mUidMap; }; } // namespace statsd diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index 4826d963a3ededc358b3e06664e81e4aeb646cbd..0dee342f0abe6441ab6192b289b4acac0c678837 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -54,8 +54,12 @@ class StatsPullerManager { mPullerManager.SetTimeBaseSec(timeBaseSec); } - void ClearPullerCache() { - mPullerManager.ClearPullerCache(); + int ForceClearPullerCache() { + return mPullerManager.ForceClearPullerCache(); + } + + int ClearPullerCacheIfNecessary(long timestampSec) { + return mPullerManager.ClearPullerCacheIfNecessary(timestampSec); } private: diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index 71b0abe25d28279a93d1782edbc15f9daf28d4dd..bee99396126d4c097c789eda27bca20480ebd44a 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true +#define DEBUG false #include "Log.h" #include @@ -24,12 +24,16 @@ #include "CpuTimePerUidFreqPuller.h" #include "CpuTimePerUidPuller.h" #include "ResourceHealthManagerPuller.h" +#include "KernelUidCpuActiveTimeReader.h" +#include "KernelUidCpuClusterTimeReader.h" +#include "SubsystemSleepStatePuller.h" #include "StatsCompanionServicePuller.h" #include "StatsPullerManagerImpl.h" #include "StatsService.h" #include "SubsystemSleepStatePuller.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" #include @@ -44,62 +48,92 @@ namespace android { namespace os { namespace statsd { +const std::map StatsPullerManagerImpl::kAllPullAtomInfo = { + // wifi_bytes_transfer + {android::util::WIFI_BYTES_TRANSFER, + {{2, 3, 4, 5}, + {}, + 1, + new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}}, + // wifi_bytes_transfer_by_fg_bg + {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, + {{3, 4, 5, 6}, + {2}, + 1, + new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}}, + // mobile_bytes_transfer + {android::util::MOBILE_BYTES_TRANSFER, + {{2, 3, 4, 5}, + {}, + 1, + new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}}, + // mobile_bytes_transfer_by_fg_bg + {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, + {{3, 4, 5, 6}, + {2}, + 1, + new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}}, + // bluetooth_bytes_transfer + {android::util::BLUETOOTH_BYTES_TRANSFER, + {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}}, + // kernel_wakelock + {android::util::KERNEL_WAKELOCK, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}}, + // subsystem_sleep_state + {android::util::SUBSYSTEM_SLEEP_STATE, {{}, {}, 1, new SubsystemSleepStatePuller()}}, + // cpu_time_per_freq + {android::util::CPU_TIME_PER_FREQ, + {{3}, {2}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}}, + // cpu_time_per_uid + {android::util::CPU_TIME_PER_UID, {{2, 3}, {}, 1, new CpuTimePerUidPuller()}}, + // cpu_time_per_uid_freq + {android::util::CPU_TIME_PER_UID_FREQ, {{3}, {2}, 1, new CpuTimePerUidFreqPuller()}}, + // wifi_activity_energy_info + {android::util::WIFI_ACTIVITY_ENERGY_INFO, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}}, + // modem_activity_info + {android::util::MODEM_ACTIVITY_INFO, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}}, + // bluetooth_activity_info + {android::util::BLUETOOTH_ACTIVITY_INFO, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}}, + // system_elapsed_realtime + {android::util::SYSTEM_ELAPSED_REALTIME, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}}, + // system_uptime + {android::util::SYSTEM_UPTIME, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}}, + // cpu_active_time + {android::util::CPU_ACTIVE_TIME, {{3}, {2}, 1, new KernelUidCpuActiveTimeReader()}}, + // cpu_cluster_time + {android::util::CPU_CLUSTER_TIME, {{3}, {2}, 1, new KernelUidCpuClusterTimeReader()}}, + // disk_space + {android::util::DISK_SPACE, + {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}}, + // remaining_battery_capacity + {android::util::REMAINING_BATTERY_CAPACITY, + {{}, {}, 1, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}}, + // full_battery_capacity + {android::util::FULL_BATTERY_CAPACITY, + {{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}, + // process_memory_state + {android::util::PROCESS_MEMORY_STATE, + {{4,5,6,7,8}, {2,3}, 0, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}}; + StatsPullerManagerImpl::StatsPullerManagerImpl() : mCurrentPullingInterval(LONG_MAX) { - mPullers.insert({android::util::KERNEL_WAKELOCK, - make_shared(android::util::KERNEL_WAKELOCK)}); - mPullers.insert({android::util::WIFI_BYTES_TRANSFER, - make_shared(android::util::WIFI_BYTES_TRANSFER)}); - mPullers.insert( - {android::util::MOBILE_BYTES_TRANSFER, - make_shared(android::util::MOBILE_BYTES_TRANSFER)}); - mPullers.insert({android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, - make_shared( - android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}); - mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, - make_shared( - android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}); - mPullers.insert( - {android::util::SUBSYSTEM_SLEEP_STATE, - make_shared()}); - mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared(android::util::CPU_TIME_PER_FREQ)}); - mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared()}); - mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared()}); - mPullers.insert( - {android::util::SYSTEM_ELAPSED_REALTIME, - make_shared(android::util::SYSTEM_ELAPSED_REALTIME)}); - mPullers.insert({android::util::SYSTEM_UPTIME, - make_shared(android::util::SYSTEM_UPTIME)}); - mPullers.insert({android::util::DISK_SPACE, - make_shared(android::util::DISK_SPACE)}); - mPullers.insert( - {android::util::BLUETOOTH_ACTIVITY_INFO, - make_shared(android::util::BLUETOOTH_ACTIVITY_INFO)}); - - mPullers.insert( - {android::util::BLUETOOTH_BYTES_TRANSFER, - make_shared(android::util::BLUETOOTH_BYTES_TRANSFER)}); - mPullers.insert( - {android::util::WIFI_ACTIVITY_ENERGY_INFO, - make_shared(android::util::WIFI_ACTIVITY_ENERGY_INFO)}); - mPullers.insert({android::util::MODEM_ACTIVITY_INFO, - make_shared(android::util::MODEM_ACTIVITY_INFO)}); - mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY, - make_shared(android::util::REMAINING_BATTERY_CAPACITY)}); - mPullers.insert({android::util::FULL_BATTERY_CAPACITY, - make_shared(android::util::FULL_BATTERY_CAPACITY)}); mStatsCompanionService = StatsService::getStatsCompanionService(); } bool StatsPullerManagerImpl::Pull(int tagId, vector>* data) { - if (DEBUG) ALOGD("Initiating pulling %d", tagId); + VLOG("Initiating pulling %d", tagId); - if (mPullers.find(tagId) != mPullers.end()) { - bool ret = mPullers.find(tagId)->second->Pull(data); - ALOGD("pulled %d items", (int)data->size()); + if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) { + bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data); + VLOG("pulled %d items", (int)data->size()); return ret; } else { - ALOGD("Unknown tagId %d", tagId); + VLOG("Unknown tagId %d", tagId); return false; // Return early since we don't know what to pull. } } @@ -110,7 +144,7 @@ StatsPullerManagerImpl& StatsPullerManagerImpl::GetInstance() { } bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const { - return mPullers.find(tagId) != mPullers.end(); + return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end(); } void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp receiver, @@ -135,8 +169,9 @@ void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp re if (roundedIntervalMs < mCurrentPullingInterval) { VLOG("Updating pulling interval %ld", intervalMs); mCurrentPullingInterval = roundedIntervalMs; - long currentTimeMs = time(nullptr) * 1000; - long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval - (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval; + long currentTimeMs = getElapsedRealtimeMillis(); + long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval - + (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval; if (mStatsCompanionService != nullptr) { mStatsCompanionService->setPullingAlarms(nextAlarmTimeMs, mCurrentPullingInterval); } else { @@ -165,7 +200,7 @@ void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp void StatsPullerManagerImpl::OnAlarmFired() { AutoMutex _l(mReceiversLock); - uint64_t currentTimeMs = time(nullptr) /60 * 60 * 1000; + uint64_t currentTimeMs = getElapsedRealtimeMillis(); vector>> needToPull = vector>>(); @@ -199,10 +234,20 @@ void StatsPullerManagerImpl::OnAlarmFired() { } } -void StatsPullerManagerImpl::ClearPullerCache() { - for (auto puller : mPullers) { - puller.second->ClearCache(); +int StatsPullerManagerImpl::ForceClearPullerCache() { + int totalCleared = 0; + for (const auto& pulledAtom : kAllPullAtomInfo) { + totalCleared += pulledAtom.second.puller->ForceClearCache(); + } + return totalCleared; +} + +int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) { + int totalCleared = 0; + for (const auto& pulledAtom : kAllPullAtomInfo) { + totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec); } + return totalCleared; } } // namespace statsd diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h index fba3ade8c1bb69dd3f82b2baa376dc8eeab1c8d6..76a4c14e13b50354c157f0eacd80777918717006 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.h +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h @@ -32,6 +32,20 @@ namespace android { namespace os { namespace statsd { +typedef struct { + // The field numbers of the fields that need to be summed when merging + // isolated uid with host uid. + std::vector additiveFields; + // The field numbers of the fields that can't be merged when merging + // data belong to isolated uid and host uid. + std::vector nonAdditiveFields; + // How long should the puller wait before doing an actual pull again. Default + // 1 sec. Set this to 0 if this is handled elsewhere. + long coolDownSec = 1; + // The actual puller + sp puller; +} PullAtomInfo; + class StatsPullerManagerImpl : public virtual RefBase { public: static StatsPullerManagerImpl& GetInstance(); @@ -49,9 +63,13 @@ public: void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;}; - void ClearPullerCache(); + int ForceClearPullerCache(); + + int ClearPullerCacheIfNecessary(long timestampSec); + + const static std::map kAllPullAtomInfo; -private: + private: StatsPullerManagerImpl(); // use this to update alarm @@ -59,9 +77,6 @@ private: sp get_stats_companion_service(); - // mapping from simple matcher tagId to puller - std::map> mPullers; - typedef struct { // pull_interval_sec : last_pull_time_sec std::pair timeInfo; @@ -79,8 +94,6 @@ private: // bucket size. All pulled metrics start pulling based on this time, so that they can be // correctly attributed to the correct buckets. long mTimeBaseSec; - - LogEvent parse_pulled_data(String16 data); }; } // namespace statsd diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp index 6d12e257df8ddef15a8c5b1fb6a3c7517789c838..4501b64ad47ece1930a9fba486411959592e2a45 100644 --- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp +++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -36,6 +36,7 @@ #include "SubsystemSleepStatePuller.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using android::hardware::hidl_vec; using android::hardware::power::V1_0::IPower; @@ -84,21 +85,24 @@ bool SubsystemSleepStatePuller::PullInternal(vector>* data) return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); data->clear(); Return ret; ret = gPowerHalV1_0->getPlatformLowPowerStats( - [&data, timestamp](hidl_vec states, Status status) { + [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec states, Status status) { if (status != Status::SUCCESS) return; for (size_t i = 0; i < states.size(); i++) { const PowerStatePlatformSleepState& state = states[i]; - auto statePtr = make_shared(android::util::SUBSYSTEM_SLEEP_STATE, - timestamp); + auto statePtr = make_shared( + android::util::SUBSYSTEM_SLEEP_STATE, + wallClockTimestampNs, elapsedTimestampNs); statePtr->write(state.name); + statePtr->write(""); statePtr->write(state.totalTransitions); statePtr->write(state.residencyInMsecSinceBoot); statePtr->init(); @@ -108,9 +112,11 @@ bool SubsystemSleepStatePuller::PullInternal(vector>* data) (long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0); for (auto voter : state.voters) { - auto voterPtr = make_shared(android::util::SUBSYSTEM_SLEEP_STATE, - timestamp); - voterPtr->write((std::string)state.name + "." + (std::string)voter.name); + auto voterPtr = make_shared( + android::util::SUBSYSTEM_SLEEP_STATE, + wallClockTimestampNs, elapsedTimestampNs); + voterPtr->write(state.name); + voterPtr->write(voter.name); voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot); voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot); voterPtr->init(); @@ -133,7 +139,7 @@ bool SubsystemSleepStatePuller::PullInternal(vector>* data) android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0); if (gPowerHal_1_1 != nullptr) { ret = gPowerHal_1_1->getSubsystemLowPowerStats( - [&data, timestamp](hidl_vec subsystems, Status status) { + [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec subsystems, Status status) { if (status != Status::SUCCESS) return; if (subsystems.size() > 0) { @@ -143,8 +149,10 @@ bool SubsystemSleepStatePuller::PullInternal(vector>* data) const PowerStateSubsystemSleepState& state = subsystem.states[j]; auto subsystemStatePtr = make_shared( - android::util::SUBSYSTEM_SLEEP_STATE, timestamp); - subsystemStatePtr->write((std::string)subsystem.name + "." + (std::string)state.name); + android::util::SUBSYSTEM_SLEEP_STATE, + wallClockTimestampNs, elapsedTimestampNs); + subsystemStatePtr->write(subsystem.name); + subsystemStatePtr->write(state.name); subsystemStatePtr->write(state.totalTransitions); subsystemStatePtr->write(state.residencyInMsecSinceBoot); subsystemStatePtr->init(); diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b0c5c41bf9dceabd4797d6c1d658ffd01449cd1 --- /dev/null +++ b/cmds/statsd/src/external/puller_util.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018 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. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "StatsPullerManagerImpl.h" +#include "puller_util.h" +#include "statslog.h" + +namespace android { +namespace os { +namespace statsd { + +using std::map; +using std::shared_ptr; +using std::vector; + +namespace { +bool shouldMerge(shared_ptr& lhs, shared_ptr& rhs, + const vector& nonAdditiveFields) { + const auto& l_values = lhs->getValues(); + const auto& r_values = rhs->getValues(); + + for (size_t i : nonAdditiveFields) { + // We store everything starting from index 0, so we need to use i-1 + if (!(l_values.size() > i - 1 && r_values.size() > i - 1 && + l_values[i - 1].mValue == r_values[i - 1].mValue)) { + return false; + } + } + return true; +} + +// merge rhs to lhs +// when calling this function, all sanity check should be done already. +// e.g., index boundary, nonAdditiveFields matching etc. +bool mergeEvent(shared_ptr& lhs, shared_ptr& rhs, + const vector& additiveFields) { + vector* host_values = lhs->getMutableValues(); + const auto& child_values = rhs->getValues(); + for (int i : additiveFields) { + Value& host = (*host_values)[i - 1].mValue; + const Value& child = (child_values[i - 1]).mValue; + if (child.getType() != host.getType()) { + return false; + } + switch (child.getType()) { + case INT: + host.setInt(host.int_value + child.int_value); + break; + case LONG: + host.setLong(host.long_value + child.long_value); + break; + default: + ALOGE("Tried to merge 2 fields with unsupported type"); + return false; + } + } + return true; +} + +bool tryMerge(vector>& data, int child_pos, const vector& host_pos, + const vector& nonAdditiveFields, const vector& additiveFields) { + for (const auto& pos : host_pos) { + if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) && + mergeEvent(data[pos], data[child_pos], additiveFields)) { + return true; + } + } + return false; +} + +} // namespace + +/** + * Process all data and merge isolated with host if necessary. + * For example: + * NetworkBytesAtom { + * int uid = 1; + * State process_state = 2; + * int byte_send = 3; + * int byte_recv = 4; + * } + * additive fields are {3, 4}, non-additive field is {2} + * If we pulled the following events (uid1_child is an isolated uid which maps to uid1): + * [uid1, fg, 100, 200] + * [uid1_child, fg, 100, 200] + * [uid1, bg, 100, 200] + * + * We want to merge them and results should be: + * [uid1, fg, 200, 400] + * [uid1, bg, 100, 200] + */ +void mergeIsolatedUidsToHostUid(vector>& data, const sp& uidMap, + int tagId) { + if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) == + StatsPullerManagerImpl::kAllPullAtomInfo.end()) { + VLOG("Unknown pull atom id %d", tagId); + return; + } + if (android::util::kAtomsWithUidField.find(tagId) == android::util::kAtomsWithUidField.end()) { + VLOG("No uid to merge for atom %d", tagId); + return; + } + const vector& additiveFields = + StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields; + const vector& nonAdditiveFields = + StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields; + + // map of host uid to their position in the original vector + map> hostPosition; + vector toRemove = vector(data.size(), false); + + for (size_t i = 0; i < data.size(); i++) { + vector* valueList = data[i]->getMutableValues(); + + int err = 0; + int uid = data[i]->GetInt(1, &err); + if (err != 0) { + VLOG("Bad uid field for %s", data[i]->ToString().c_str()); + return; + } + + const int hostUid = uidMap->getHostUidOrSelf(uid); + + if (hostUid != uid) { + (*valueList)[0].mValue.setInt(hostUid); + } + if (hostPosition.find(hostUid) == hostPosition.end()) { + hostPosition[hostUid].push_back(i); + } else { + if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) { + toRemove[i] = true; + } else { + hostPosition[hostUid].push_back(i); + } + } + } + + vector> mergedData; + for (size_t i = 0; i < toRemove.size(); i++) { + if (!toRemove[i]) { + mergedData.push_back(data[i]); + } + } + data.clear(); + data = mergedData; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/statsd_internal.proto b/cmds/statsd/src/external/puller_util.h similarity index 56% rename from cmds/statsd/src/statsd_internal.proto rename to cmds/statsd/src/external/puller_util.h index 25aacee2770a1365fbd0dd6660bbb20511c92986..fd4a4a2a57555ccdeb7a55b553b07e196e811f42 100644 --- a/cmds/statsd/src/statsd_internal.proto +++ b/cmds/statsd/src/external/puller_util.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 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. @@ -14,16 +14,20 @@ * limitations under the License. */ -syntax = "proto2"; -option optimize_for = LITE_RUNTIME; +#pragma once -package android.os.statsd; +#include +#include "StatsPuller.h" +#include "logd/LogEvent.h" +#include "packages/UidMap.h" -option java_package = "com.android.os"; -option java_outer_classname = "StatsdInternalProto"; +namespace android { +namespace os { +namespace statsd { -message Field { - optional int32 field = 1; - optional int32 position_index = 2 [default = -1]; - repeated Field child = 3; -} +void mergeIsolatedUidsToHostUid(std::vector>& data, + const sp& uidMap, int tagId); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp deleted file mode 100644 index acf64fe12e6db75b06d83916b0c3974e6e04406d..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/field_util.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#include "Log.h" -#include "field_util.h" - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -// This function is to compare two Field trees where each node has at most one child. -bool CompareField(const Field& a, const Field& b) { - if (a.field() < b.field()) { - return true; - } - if (a.field() > b.field()) { - return false; - } - if (a.position_index() < b.position_index()) { - return true; - } - if (a.position_index() > b.position_index()) { - return false; - } - if (a.child_size() < b.child_size()) { - return true; - } - if (a.child_size() > b.child_size()) { - return false; - } - if (a.child_size() == 0 && b.child_size() == 0) { - return false; - } - return CompareField(a.child(0), b.child(0)); -} - -const Field* getSingleLeaf(const Field* field) { - if (field->child_size() <= 0) { - return field; - } else { - return getSingleLeaf(&field->child(0)); - } -} - -Field* getSingleLeaf(Field* field) { - if (field->child_size() <= 0) { - return field; - } else { - return getSingleLeaf(field->mutable_child(0)); - } -} - -void FieldToString(const Field& field, std::string *flattened) { - *flattened += std::to_string(field.field()); - if (field.has_position_index()) { - *flattened += "["; - *flattened += std::to_string(field.position_index()); - *flattened += "]"; - } - if (field.child_size() <= 0) { - return; - } - *flattened += "."; - *flattened += "{"; - for (int i = 0 ; i < field.child_size(); ++i) { - *flattened += FieldToString(field.child(i)); - } - *flattened += "},"; -} - -std::string FieldToString(const Field& field) { - std::string flatten; - FieldToString(field, &flatten); - return flatten; -} - -bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue) { - if (field.child_size() <= 0) { - leafValue->set_field(field.field()); - return true; - } else if (field.child_size() == 1) { - return setFieldInLeafValueProto(field.child(0), leafValue); - } else { - ALOGE("Not able to set the 'field' in leaf value for multiple children."); - return false; - } -} - -void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field *field) { - field->set_field(tagId); - field->add_child()->set_field(atomFieldNum); -} - -void buildSimpleAtomField(const int tagId, Field *field) { - field->set_field(tagId); -} - -void appendLeaf(Field *parent, int node_field_num) { - if (!parent->has_field()) { - parent->set_field(node_field_num); - } else if (parent->child_size() <= 0) { - parent->add_child()->set_field(node_field_num); - } else { - appendLeaf(parent->mutable_child(0), node_field_num); - } -} - -void appendLeaf(Field *parent, int node_field_num, int position) { - if (!parent->has_field()) { - parent->set_field(node_field_num); - parent->set_position_index(position); - } else if (parent->child_size() <= 0) { - auto child = parent->add_child(); - child->set_field(node_field_num); - child->set_position_index(position); - } else { - appendLeaf(parent->mutable_child(0), node_field_num, position); - } -} - -void increasePosition(Field *field) { - if (!field->has_position_index()) { - field->set_position_index(0); - } else { - field->set_position_index(field->position_index() + 1); - } -} - -int getPositionByReferenceField(const Field& ref, const Field& field_with_index) { - if (ref.child_size() <= 0) { - return field_with_index.position_index(); - } - if (ref.child_size() != 1 || - field_with_index.child_size() != 1) { - return -1; - } - return getPositionByReferenceField(ref.child(0), field_with_index.child(0)); -} - -namespace { - -void findFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::set* rootFields); - -void findNonRepeatedFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::set* rootFields) { - if (matcher.child_size() > 0) { - Field* newLeafField = leafField->add_child(); - for (const auto& childMatcher : matcher.child()) { - newLeafField->set_field(childMatcher.field()); - findFields(fieldValueMap, childMatcher, rootField, newLeafField, rootFields); - } - leafField->clear_child(); - } else { - auto ret = fieldValueMap.equal_range(*rootField); - int found = 0; - for (auto it = ret.first; it != ret.second; ++it) { - found++; - } - // Not found. - if (found <= 0) { - return; - } - if (found > 1) { - ALOGE("Found multiple values for optional field."); - return; - } - rootFields->insert(ret.first->first); - } -} - -void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher, - Field* rootField, Field* leafField, - std::set* rootFields) { - if (matcher.position() == Position::FIRST) { - leafField->set_position_index(0); - findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields); - leafField->clear_position_index(); - } else { - auto itLower = fieldValueMap.lower_bound(*rootField); - if (itLower == fieldValueMap.end()) { - return; - } - - const int leafFieldNum = leafField->field(); - leafField->set_field(leafFieldNum + 1); - auto itUpper = fieldValueMap.lower_bound(*rootField); - // Resets the field number. - leafField->set_field(leafFieldNum); - - switch (matcher.position()) { - case Position::LAST: - { - itUpper--; - if (itUpper != fieldValueMap.end()) { - int last_index = getPositionByReferenceField(*rootField, itUpper->first); - if (last_index < 0) { - return; - } - leafField->set_position_index(last_index); - findNonRepeatedFields( - fieldValueMap, matcher, rootField, leafField, rootFields); - leafField->clear_position_index(); - } - } - break; - case Position::ANY: - { - std::set indexes; - for (auto it = itLower; it != itUpper; ++it) { - int index = getPositionByReferenceField(*rootField, it->first); - if (index >= 0) { - indexes.insert(index); - } - } - if (!indexes.empty()) { - for (const int index : indexes) { - leafField->set_position_index(index); - findNonRepeatedFields( - fieldValueMap, matcher, rootField, leafField, rootFields); - leafField->clear_position_index(); - } - } - } - break; - default: - break; - } - } -} - -void findFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - Field* rootField, - Field* leafField, - std::set* rootFields) { - if (!matcher.has_position()) { - findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields); - } else { - findRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields); - } -} - -} // namespace - -void findFields( - const FieldValueMap& fieldValueMap, - const FieldMatcher& matcher, - std::set* rootFields) { - if (!matcher.has_field() || fieldValueMap.empty()) { - return; - } - Field rootField; - buildSimpleAtomField(matcher.field(), &rootField); - return findFields(fieldValueMap, matcher, &rootField, &rootField, rootFields); -} - -void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) { - if (!matcher.has_field()) { - return; - } - std::set rootFields; - findFields(*fieldValueMap, matcher, &rootFields); - auto it = fieldValueMap->begin(); - while (it != fieldValueMap->end()) { - if (rootFields.find(it->first) == rootFields.end()) { - it = fieldValueMap->erase(it); - } else { - it++; - } - } -} - -bool hasLeafNode(const FieldMatcher& matcher) { - if (!matcher.has_field()) { - return false; - } - for (int i = 0; i < matcher.child_size(); ++i) { - if (hasLeafNode(matcher.child(i))) { - return true; - } - } - return true; -} - -bool IsAttributionUidField(const Field& field) { - return field.child_size() == 1 && field.child(0).field() == 1 - && field.child(0).child_size() == 1 && field.child(0).child(0).field() == 1; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h deleted file mode 100644 index b04465dc986251188e77ed054b6e949dbaf42221..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/field_util.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" - -#include -#include - -namespace android { -namespace os { -namespace statsd { - -// Function to sort the Field protos. -bool CompareField(const Field& a, const Field& b); -struct FieldCmp { - bool operator()(const Field& a, const Field& b) const { - return CompareField(a, b); - } -}; - -// Flattened dimensions value map. To save space, usually the key contains the tree structure info -// and value field is only leaf node. -typedef std::map FieldValueMap; - -// Util function to print the Field proto. -std::string FieldToString(const Field& field); - -// Util function to find the leaf node from the input Field proto and set it in the corresponding -// value proto. -bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue); - -// Returns the leaf node from the Field proto. It assume that the input has only one -// leaf node at most. -const Field* getSingleLeaf(const Field* field); -Field* getSingleLeaf(Field* field); - -// Append a node to the current leaf. It assumes that the input "parent" has one leaf node at most. -void appendLeaf(Field *parent, int node_field_num); -void appendLeaf(Field *parent, int node_field_num, int position); - -// Increase the position index for the node. If the "position_index" is not set, set it as 0. -void increasePosition(Field *field); - -// Returns true if the matcher has specified at least one leaf node. -bool hasLeafNode(const FieldMatcher& matcher); - -// The two input Field proto are describing the same tree structure. Both contain one leaf node at -// most. This is find the position index info for the leaf node at "reference" stored in the -// "field_with_index" tree. -int getPositionByReferenceField(const Field& reference, const Field& field_with_index); - -// Utils to build the Field proto for simple atom fields. -void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field* field); -void buildSimpleAtomField(const int tagId, Field* field); - -// Find out all the fields specified by the matcher. -void findFields( - const FieldValueMap& fieldValueMap, const FieldMatcher& matcher, - std::set* rootFields); - -// Filter out the fields not in the field matcher. -void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap); - -// Returns if the field is attribution node uid field. -bool IsAttributionUidField(const Field& field); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp b/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp index e1947c4a5fe848da3b1002b7cfa60563574aca72..01c75873fdf9d0fa51d0b7d12289bb41f3285de7 100644 --- a/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp +++ b/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -63,7 +63,7 @@ std::string dumpMemInfo(size_t limit) { size_t count; if (info == nullptr || overallSize == 0 || infoSize == 0 || (count = overallSize / infoSize) == 0) { - ALOGD("no malloc info, libc.debug.malloc.program property should be set"); + VLOG("no malloc info, libc.debug.malloc.program property should be set"); return std::string(); } diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 77f54569cb4778cffad58646a502718cda0aaa00..66cb1d04a4e1852a879e74dd852c7d2558d35be2 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "StatsdStats.h" @@ -45,18 +45,9 @@ const int FIELD_ID_CONFIG_STATS = 3; const int FIELD_ID_ATOM_STATS = 7; const int FIELD_ID_UIDMAP_STATS = 8; const int FIELD_ID_ANOMALY_ALARM_STATS = 9; -const int FIELD_ID_PULLED_ATOM_STATS = 10; +// const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp const int FIELD_ID_LOGGER_ERROR_STATS = 11; -const int FIELD_ID_MATCHER_STATS_NAME = 1; -const int FIELD_ID_MATCHER_STATS_COUNT = 2; - -const int FIELD_ID_CONDITION_STATS_NAME = 1; -const int FIELD_ID_CONDITION_STATS_COUNT = 2; - -const int FIELD_ID_METRIC_STATS_NAME = 1; -const int FIELD_ID_METRIC_STATS_COUNT = 2; - const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; @@ -80,7 +71,7 @@ std::map StatsdStats::kPullerCooldownMap = { // TODO: add stats for pulled atoms. StatsdStats::StatsdStats() { mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1); - mStartTimeSec = time(nullptr); + mStartTimeSec = getWallClockSec(); } StatsdStats& StatsdStats::getInstance() { @@ -99,7 +90,7 @@ void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount, int alertsCount, bool isValid) { lock_guard lock(mLock); - int32_t nowTimeSec = time(nullptr); + int32_t nowTimeSec = getWallClockSec(); // If there is an existing config for the same key, icebox the old config. noteConfigRemovedInternalLocked(key); @@ -125,7 +116,7 @@ void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { auto it = mConfigStats.find(key); if (it != mConfigStats.end()) { - int32_t nowTimeSec = time(nullptr); + int32_t nowTimeSec = getWallClockSec(); it->second.set_deletion_time_sec(nowTimeSec); // Add condition stats, metrics stats, matcher stats, alert stats addSubStatsToConfigLocked(key, it->second); @@ -145,7 +136,7 @@ void StatsdStats::noteConfigRemoved(const ConfigKey& key) { } void StatsdStats::noteBroadcastSent(const ConfigKey& key) { - noteBroadcastSent(key, time(nullptr)); + noteBroadcastSent(key, getWallClockSec()); } void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { @@ -164,7 +155,7 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { } void StatsdStats::noteDataDropped(const ConfigKey& key) { - noteDataDropped(key, time(nullptr)); + noteDataDropped(key, getWallClockSec()); } void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) { @@ -183,7 +174,7 @@ void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) { } void StatsdStats::noteMetricsReportSent(const ConfigKey& key) { - noteMetricsReportSent(key, time(nullptr)); + noteMetricsReportSent(key, getWallClockSec()); } void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) { @@ -275,10 +266,6 @@ void StatsdStats::notePullFromCache(int pullAtomId) { void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) { lock_guard lock(mLock); - if (timeSec < mStartTimeSec) { - return; - } - if (atomId > android::util::kMaxPushedAtomId) { ALOGW("not interested in atom %d", atomId); return; @@ -293,7 +280,7 @@ void StatsdStats::noteLoggerError(int error) { if (mLoggerErrors.size() == kMaxLoggerErrors) { mLoggerErrors.pop_front(); } - mLoggerErrors.push_back(std::make_pair(time(nullptr), error)); + mLoggerErrors.push_back(std::make_pair(getWallClockSec(), error)); } void StatsdStats::reset() { @@ -303,7 +290,7 @@ void StatsdStats::reset() { void StatsdStats::resetInternalLocked() { // Reset the historical data, but keep the active ConfigStats - mStartTimeSec = time(nullptr); + mStartTimeSec = getWallClockSec(); mIceBox.clear(); mConditionStats.clear(); mMetricsStats.clear(); @@ -495,7 +482,7 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { ProtoOutputStream proto; proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr)); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec()); for (const auto& configStats : mIceBox) { const int numBytes = configStats.ByteSize(); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 1f4bfa62c4533f07996f8ffd2dda98c61ad216a8..8c16e4e9c2eb64fbf9400ca8b89a2becc7d9be4f 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -16,7 +16,7 @@ #pragma once #include "config/ConfigKey.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "statslog.h" #include @@ -42,6 +42,7 @@ public: const static int kDimensionKeySizeHardLimit = 500; const static int kMaxConfigCount = 10; + const static int kMaxAlertCountPerConfig = 100; const static int kMaxConditionCountPerConfig = 200; const static int kMaxMetricCountPerConfig = 300; const static int kMaxMatcherCountPerConfig = 500; @@ -85,6 +86,9 @@ public: // Maximum size of all files that can be written to stats directory on disk. static const int kMaxFileSize = 50 * 1024 * 1024; + // How long to try to clear puller cache from last time + static const long kPullerCacheClearIntervalSec = 1; + /** * Report a new config has been received and report the static stats about the config. * diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 1dcd8534db79fb76170cdb89e0f2b2140811b65e..d282b86f52bdf7a9459a734fae567ab1b32f600c 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -14,16 +14,11 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "logd/LogEvent.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" - -#include #include -#include "field_util.h" -#include "dimension.h" #include "stats_log_util.h" namespace android { @@ -31,14 +26,15 @@ namespace os { namespace statsd { using namespace android::util; +using android::util::ProtoOutputStream; using std::ostringstream; using std::string; -using android::util::ProtoOutputStream; +using std::vector; LogEvent::LogEvent(log_msg& msg) { mContext = create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t)); - mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; + mLogdTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; mLogUid = msg.entry_v4.uid; init(mContext); if (mContext) { @@ -47,12 +43,24 @@ LogEvent::LogEvent(log_msg& msg) { } } -LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) { - mTimestampNs = timestampNs; +LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) { + mLogdTimestampNs = wallClockTimestampNs; mTagId = tagId; mLogUid = 0; mContext = create_android_logger(1937006964); // the event tag shared by all stats logs if (mContext) { + android_log_write_int64(mContext, elapsedTimestampNs); + android_log_write_int32(mContext, tagId); + } +} + +LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) { + mLogdTimestampNs = timestampNs; + mTagId = tagId; + mLogUid = 0; + mContext = create_android_logger(1937006964); // the event tag shared by all stats logs + if (mContext) { + android_log_write_int64(mContext, timestampNs); android_log_write_int32(mContext, tagId); } } @@ -123,7 +131,7 @@ bool LogEvent::write(float value) { return false; } -bool LogEvent::write(const std::vector& nodes) { +bool LogEvent::write(const std::vector& nodes) { if (mContext) { if (android_log_write_list_begin(mContext) < 0) { return false; @@ -141,7 +149,7 @@ bool LogEvent::write(const std::vector& nodes) { return false; } -bool LogEvent::write(const AttributionNode& node) { +bool LogEvent::write(const AttributionNodeInternal& node) { if (mContext) { if (android_log_write_list_begin(mContext) < 0) { return false; @@ -152,9 +160,6 @@ bool LogEvent::write(const AttributionNode& node) { if (android_log_write_string8(mContext, node.tag().c_str()) < 0) { return false; } - if (android_log_write_int32(mContext, node.uid()) < 0) { - return false; - } if (android_log_write_list_end(mContext) < 0) { return false; } @@ -163,103 +168,104 @@ bool LogEvent::write(const AttributionNode& node) { return false; } -namespace { - -void increaseField(Field *field, bool is_child) { - if (is_child) { - if (field->child_size() <= 0) { - field->add_child(); - } - } else { - field->clear_child(); - } - Field* curr = is_child ? field->mutable_child(0) : field; - if (!curr->has_field()) { - curr->set_field(1); - } else { - curr->set_field(curr->field() + 1); - } -} - -} // namespace - /** * The elements of each log event are stored as a vector of android_log_list_elements. * The goal is to do as little preprocessing as possible, because we read a tiny fraction * of the elements that are written to the log. + * + * The idea here is to read through the log items once, we get as much information we need for + * matching as possible. Because this log will be matched against lots of matchers. */ void LogEvent::init(android_log_context context) { - if (!context) { - return; - } android_log_list_element elem; - // TODO: The log is actually structured inside one list. This is convenient - // because we'll be able to use it to put the attribution (WorkSource) block first - // without doing our own tagging scheme. Until that change is in, just drop the - // list-related log elements and the order we get there is our index-keyed data - // structure. int i = 0; - - int seenListStart = 0; - - Field fieldTree; - Field* atomField = fieldTree.add_child(); + int depth = -1; + int pos[] = {1, 1, 1}; do { elem = android_log_read_next(context); switch ((int)elem.type) { case EVENT_TYPE_INT: - // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. - if (i == 1) { + // elem at [0] is EVENT_TYPE_LIST, [1] is the timestamp, [2] is tag id. + if (i == 2) { mTagId = elem.data.int32; - fieldTree.set_field(mTagId); } else { - increaseField(atomField, seenListStart > 0/* is_child */); - mFieldValueMap[fieldTree].set_value_int(elem.data.int32); - } - break; - case EVENT_TYPE_FLOAT: - { - increaseField(atomField, seenListStart > 0/* is_child */); - mFieldValueMap[fieldTree].set_value_float(elem.data.float32); + if (depth < 0 || depth > 2) { + return; + } + + mValues.push_back( + FieldValue(Field(mTagId, pos, depth), Value((int32_t)elem.data.int32))); + + pos[depth]++; } break; - case EVENT_TYPE_STRING: - { - increaseField(atomField, seenListStart > 0/* is_child */); - mFieldValueMap[fieldTree].set_value_str( - string(elem.data.string, elem.len).c_str()); + case EVENT_TYPE_FLOAT: { + if (depth < 0 || depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; } - break; - case EVENT_TYPE_LONG: - { - increaseField(atomField, seenListStart > 0 /* is_child */); - mFieldValueMap[fieldTree].set_value_long(elem.data.int64); + + mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32))); + + pos[depth]++; + + } break; + case EVENT_TYPE_STRING: { + if (depth < 0 || depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; } - break; - case EVENT_TYPE_LIST: - if (i >= 1) { - if (seenListStart > 0) { - increasePosition(atomField); - } else { - increaseField(atomField, false /* is_child */); - } - seenListStart++; - if (seenListStart >= 3) { + + mValues.push_back(FieldValue(Field(mTagId, pos, depth), + Value(string(elem.data.string, elem.len)))); + + pos[depth]++; + + } break; + case EVENT_TYPE_LONG: { + if (i == 1) { + mElapsedTimestampNs = elem.data.int64; + } else { + if (depth < 0 || depth > 2) { ALOGE("Depth > 2. Not supported!"); return; } + mValues.push_back( + FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64))); + + pos[depth]++; } + } break; + case EVENT_TYPE_LIST: + depth++; + if (depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; + } + pos[depth] = 1; + break; - case EVENT_TYPE_LIST_STOP: - seenListStart--; - if (seenListStart == 0) { - atomField->clear_position_index(); - } else { - if (atomField->child_size() > 0) { - atomField->mutable_child(0)->clear_field(); + case EVENT_TYPE_LIST_STOP: { + int prevDepth = depth; + depth--; + if (depth >= 0 && depth < 2) { + // Now go back to decorate the previous items that are last at prevDepth. + // So that we can later easily match them with Position=Last matchers. + pos[prevDepth]--; + int path = getEncodedField(pos, prevDepth, false); + for (auto it = mValues.rbegin(); it != mValues.rend(); ++it) { + if (it->mField.getDepth() >= prevDepth && + it->mField.getPath(prevDepth) == path) { + it->mField.decorateLastPos(prevDepth); + } else { + // Safe to break, because the items are in DFS order. + break; + } } + pos[depth]++; } break; + } case EVENT_TYPE_UNKNOWN: break; default: @@ -270,140 +276,117 @@ void LogEvent::init(android_log_context context) { } int64_t LogEvent::GetLong(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return (int64_t)leafValue->value_int(); - case DimensionsValue::ValueCase::kValueLong: - return leafValue->value_long(); - case DimensionsValue::ValueCase::kValueBool: - return leafValue->value_bool() ? 1 : 0; - case DimensionsValue::ValueCase::kValueFloat: - return (int64_t)leafValue->value_float(); - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; + // TODO: encapsulate the magical operations all in Field struct as a static function. + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == LONG) { + return value.mValue.long_value; + } else if (value.mValue.getType() == INT) { + return value.mValue.int_value; + } else { + *err = BAD_TYPE; + return 0; + } } - } -} - -const char* LogEvent::GetString(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return leafValue->value_str().c_str(); - case DimensionsValue::ValueCase::kValueInt: - case DimensionsValue::ValueCase::kValueLong: - case DimensionsValue::ValueCase::kValueBool: - case DimensionsValue::ValueCase::kValueFloat: - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; } } + + *err = BAD_INDEX; + return 0; } -bool LogEvent::GetBool(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return leafValue->value_int() != 0; - case DimensionsValue::ValueCase::kValueLong: - return leafValue->value_long() != 0; - case DimensionsValue::ValueCase::kValueBool: - return leafValue->value_bool(); - case DimensionsValue::ValueCase::kValueFloat: - return leafValue->value_float() != 0; - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; +int LogEvent::GetInt(size_t key, status_t* err) const { + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == INT) { + return value.mValue.int_value; + } else { + *err = BAD_TYPE; + return 0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; } } + + *err = BAD_INDEX; + return 0; } -float LogEvent::GetFloat(size_t key, status_t* err) const { - DimensionsValue value; - if (!GetSimpleAtomDimensionsValueProto(key, &value)) { - *err = BAD_INDEX; - return 0; - } - const DimensionsValue* leafValue = getSingleLeafValue(&value); - switch (leafValue->value_case()) { - case DimensionsValue::ValueCase::kValueInt: - return (float)leafValue->value_int(); - case DimensionsValue::ValueCase::kValueLong: - return (float)leafValue->value_long(); - case DimensionsValue::ValueCase::kValueBool: - return leafValue->value_bool() ? 1.0f : 0.0f; - case DimensionsValue::ValueCase::kValueFloat: - return leafValue->value_float(); - case DimensionsValue::ValueCase::kValueTuple: - case DimensionsValue::ValueCase::kValueStr: - case DimensionsValue::ValueCase::VALUE_NOT_SET: { - *err = BAD_TYPE; - return 0; +const char* LogEvent::GetString(size_t key, status_t* err) const { + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == STRING) { + return value.mValue.str_value.c_str(); + } else { + *err = BAD_TYPE; + return 0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; } } -} -void LogEvent::GetAtomDimensionsValueProtos(const FieldMatcher& matcher, - std::vector *dimensionsValues) const { - findDimensionsValues(mFieldValueMap, matcher, dimensionsValues); + *err = BAD_INDEX; + return NULL; } -bool LogEvent::GetAtomDimensionsValueProto(const FieldMatcher& matcher, - DimensionsValue* dimensionsValue) const { - std::vector rootDimensionsValues; - findDimensionsValues(mFieldValueMap, matcher, &rootDimensionsValues); - if (rootDimensionsValues.size() != 1) { - return false; +bool LogEvent::GetBool(size_t key, status_t* err) const { + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == INT) { + return value.mValue.int_value != 0; + } else if (value.mValue.getType() == LONG) { + return value.mValue.long_value != 0; + } else { + *err = BAD_TYPE; + return false; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; + } } - *dimensionsValue = rootDimensionsValues.front(); - return true; -} -bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField, - DimensionsValue* dimensionsValue) const { - FieldMatcher matcher; - buildSimpleAtomFieldMatcher(mTagId, atomField, &matcher); - return GetAtomDimensionsValueProto(matcher, dimensionsValue); + *err = BAD_INDEX; + return false; } -DimensionsValue* LogEvent::findFieldValueOrNull(const Field& field) { - auto it = mFieldValueMap.find(field); - if (it == mFieldValueMap.end()) { - return nullptr; +float LogEvent::GetFloat(size_t key, status_t* err) const { + int field = getSimpleField(key); + for (const auto& value : mValues) { + if (value.mField.getField() == field) { + if (value.mValue.getType() == FLOAT) { + return value.mValue.float_value; + } else { + *err = BAD_TYPE; + return 0.0; + } + } + if ((size_t)value.mField.getPosAtDepth(0) > key) { + break; + } } - return &it->second; + + *err = BAD_INDEX; + return 0.0; } string LogEvent::ToString() const { ostringstream result; - result << "{ " << mTimestampNs << " (" << mTagId << ")"; - for (const auto& itr : mFieldValueMap) { - result << FieldToString(itr.first); + result << "{ " << mLogdTimestampNs << " " << mElapsedTimestampNs << " (" << mTagId << ")"; + for (const auto& value : mValues) { + result << StringPrintf("%#x", value.mField.getField()); result << "->"; - result << DimensionsValueToString(itr.second); + result << value.mValue.toString(); result << " "; } result << " }"; @@ -411,7 +394,7 @@ string LogEvent::ToString() const { } void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { - writeFieldValueTreeToStream(getFieldValueMap(), &protoOutput); + writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput); } } // namespace statsd diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index eb2c00845d00e4089421b1d270dca9aef74b87fe..24d624d9d9be45641dad09885ca11ffae0239c9f 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -16,28 +16,41 @@ #pragma once -#include "field_util.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "FieldValue.h" #include #include #include #include #include -#include -#include #include -#include #include namespace android { namespace os { namespace statsd { -using std::string; -using std::vector; +struct AttributionNodeInternal { + void set_uid(int32_t id) { + mUid = id; + } + + void set_tag(const std::string& value) { + mTag = value; + } + + int32_t uid() const { + return mUid; + } + + const std::string& tag() const { + return mTag; + } + int32_t mUid; + std::string mTag; +}; /** * Wrapper for the log_msg structure. */ @@ -51,14 +64,18 @@ public: /** * Constructs a LogEvent with synthetic data for testing. Must call init() before reading. */ - explicit LogEvent(int32_t tagId, uint64_t timestampNs); + explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs); + + // For testing. The timestamp is used as both elapsed real time and logd timestamp. + explicit LogEvent(int32_t tagId, int64_t timestampNs); ~LogEvent(); /** * Get the timestamp associated with this event. */ - inline uint64_t GetTimestampNs() const { return mTimestampNs; } + inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; } + inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; } /** * Get the tag for this event. @@ -76,23 +93,11 @@ public: * Returns BAD_TYPE if the index is available but the data is the wrong type. */ int64_t GetLong(size_t key, status_t* err) const; + int GetInt(size_t key, status_t* err) const; const char* GetString(size_t key, status_t* err) const; bool GetBool(size_t key, status_t* err) const; float GetFloat(size_t key, status_t* err) const; - /* - * Get DimensionsValue proto objects from FieldMatcher. - */ - void GetAtomDimensionsValueProtos( - const FieldMatcher& matcher, std::vector *dimensionsValues) const; - bool GetAtomDimensionsValueProto( - const FieldMatcher& matcher, DimensionsValue* dimensionsValue) const; - - /* - * Get a DimensionsValue proto objects from Field. - */ - bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const; - /** * Write test data to the LogEvent. This can only be used when the LogEvent is constructed * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it. @@ -101,15 +106,15 @@ public: bool write(int32_t value); bool write(uint64_t value); bool write(int64_t value); - bool write(const string& value); + bool write(const std::string& value); bool write(float value); - bool write(const std::vector& nodes); - bool write(const AttributionNode& node); + bool write(const std::vector& nodes); + bool write(const AttributionNodeInternal& node); /** * Return a string representation of this event. */ - string ToString() const; + std::string ToString() const; /** * Write this object to a ProtoOutputStream. @@ -123,20 +128,30 @@ public: void init(); /** - * Set timestamp if the original timestamp is missing. + * Set elapsed timestamp if the original timestamp is missing. */ - void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;} - - inline int size() const { - return mFieldValueMap.size(); + void setElapsedTimestampNs(int64_t timestampNs) { + mElapsedTimestampNs = timestampNs; } /** - * Returns the mutable DimensionsValue proto for the specific the field. + * Set the timestamp if the original logd timestamp is missing. */ - DimensionsValue* findFieldValueOrNull(const Field& field); + void setLogdWallClockTimestampNs(int64_t timestampNs) { + mLogdTimestampNs = timestampNs; + } - inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; } + inline int size() const { + return mValues.size(); + } + + const std::vector& getValues() const { + return mValues; + } + + std::vector* getMutableValues() { + return &mValues; + } private: /** @@ -150,14 +165,20 @@ private: */ void init(android_log_context context); - FieldValueMap mFieldValueMap; + // The items are naturally sorted in DFS order as we read them. this allows us to do fast + // matching. + std::vector mValues; // This field is used when statsD wants to create log event object and write fields to it. After // calling init() function, this object would be destroyed to save memory usage. // When the log event is created from log msg, this field is never initiated. android_log_context mContext = NULL; - uint64_t mTimestampNs; + // The timestamp set by the logd. + int64_t mLogdTimestampNs; + + // The elapsed timestamp set by statsd log writer. + int64_t mElapsedTimestampNs; int mTagId; diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index fae91729fe4ff7b8918080fb5c44c8cbcfd57314..461200905a2590ba39c8d1a5dc5a2ec1c7837618 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -13,23 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/LogMatchingTracker.h" #include "matchers/matcher_util.h" -#include "dimension.h" #include "stats_util.h" -#include "field_util.h" - -#include -#include -#include -#include - -#include -#include using std::ostringstream; using std::set; @@ -93,198 +83,249 @@ bool combinationMatch(const vector& children, const LogicalOperation& opera return matched; } -namespace { +bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value, + const string& str_match) { + if (isAttributionUidField(field, value)) { + int uid = value.int_value; + std::set packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/); + return packageNames.find(str_match) != packageNames.end(); + } else if (value.getType() == STRING) { + return value.str_value == str_match; + } + return false; +} -bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, Field* rootField, Field* leafField); +bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, + const vector& values, int start, int end, int depth) { + if (depth > 2) { + ALOGE("Depth > 3 not supported"); + return false; + } -bool matchesNonRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, Field* rootField, Field* leafField) { - if (matcher.value_matcher_case() == - FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) { - return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field(); - } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) { - bool allMatched = true; - Field* newLeafField = leafField->add_child(); - for (int i = 0; allMatched && i < matcher.matches_tuple().field_value_matcher_size(); ++i) { - const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i); - newLeafField->set_field(childMatcher.field()); - allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, rootField, newLeafField); - } - leafField->clear_child(); - return allMatched; - } else { - auto ret = fieldMap.equal_range(*rootField); - int found = 0; - for (auto it = ret.first; it != ret.second; ++it) { - found++; - } - // Not found. - if (found <= 0) { - return false; + if (start >= end) { + return false; + } + + // Filter by entry field first + int newStart = -1; + int newEnd = end; + // because the fields are naturally sorted in the DFS order. we can safely + // break when pos is larger than the one we are searching for. + for (int i = start; i < end; i++) { + int pos = values[i].mField.getPosAtDepth(depth); + if (pos == matcher.field()) { + if (newStart == -1) { + newStart = i; + } + newEnd = i + 1; + } else if (pos > matcher.field()) { + break; } - if (found > 1) { - ALOGE("Found multiple values for optional field."); + } + + // Now we have zoomed in to a new range + start = newStart; + end = newEnd; + + if (start == -1) { + // No such field found. + return false; + } + + vector> ranges; // the ranges are for matching ANY position + if (matcher.has_position()) { + // Repeated fields position is stored as a node in the path. + depth++; + if (depth > 2) { return false; } - bool matched = false; - switch (matcher.value_matcher_case()) { - case FieldValueMatcher::ValueMatcherCase::kEqBool: { - // Logd does not support bool, it is int instead. - matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool()); + switch (matcher.position()) { + case Position::FIRST: { + for (int i = start; i < end; i++) { + int pos = values[i].mField.getPosAtDepth(depth); + if (pos != 1) { + // Again, the log elements are stored in sorted order. so + // once the position is > 1, we break; + end = i; + break; + } + } + ranges.push_back(std::make_pair(start, end)); break; } - case FieldValueMatcher::ValueMatcherCase::kEqString: { - if (IsAttributionUidField(*rootField)) { - const int uid = ret.first->second.value_int(); - std::set packageNames = - uidMap.getAppNamesFromUid(uid, true /* normalize*/); - matched = packageNames.find(matcher.eq_string()) != packageNames.end(); - } else { - matched = (ret.first->second.value_str() == matcher.eq_string()); + case Position::LAST: { + // move the starting index to the first LAST field at the depth. + for (int i = start; i < end; i++) { + if (values[i].mField.isLastPos(depth)) { + start = i; + break; + } } + ranges.push_back(std::make_pair(start, end)); break; } - case FieldValueMatcher::ValueMatcherCase::kEqInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val == matcher.eq_int()); + case Position::ANY: { + // ANY means all the children matchers match in any of the sub trees, it's a match + newStart = start; + newEnd = end; + // Here start is guaranteed to be a valid index. + int currentPos = values[start].mField.getPosAtDepth(depth); + // Now find all sub trees ranges. + for (int i = start; i < end; i++) { + int newPos = values[i].mField.getPosAtDepth(depth); + if (newPos != currentPos) { + ranges.push_back(std::make_pair(newStart, i)); + newStart = i; + currentPos = newPos; + } + } + ranges.push_back(std::make_pair(newStart, end)); break; } - case FieldValueMatcher::ValueMatcherCase::kLtInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val < matcher.lt_int()); + case Position::POSITION_UNKNOWN: break; + } + } else { + // No position + ranges.push_back(std::make_pair(start, end)); + } + // start and end are still pointing to the matched range. + switch (matcher.value_matcher_case()) { + case FieldValueMatcher::kMatchesTuple: { + ++depth; + // If any range matches all matchers, good. + for (const auto& range : ranges) { + bool matched = true; + for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) { + if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second, + depth)) { + matched = false; + break; + } + } + if (matched) return true; } - case FieldValueMatcher::ValueMatcherCase::kGtInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val > matcher.gt_int()); - break; + return false; + } + case FieldValueMatcher::ValueMatcherCase::kEqBool: { + for (int i = start; i < end; i++) { + if ((values[i].mValue.getType() == INT && + (values[i].mValue.int_value != 0) == matcher.eq_bool()) || + (values[i].mValue.getType() == LONG && + (values[i].mValue.long_value != 0) == matcher.eq_bool())) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kLtFloat: { - matched = (ret.first->second.value_float() < matcher.lt_float()); - break; + return false; + } + case FieldValueMatcher::ValueMatcherCase::kEqString: { + for (int i = start; i < end; i++) { + if (tryMatchString(uidMap, values[i].mField, values[i].mValue, + matcher.eq_string())) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kGtFloat: { - matched = (ret.first->second.value_float() > matcher.gt_float()); - break; + } + return false; + case FieldValueMatcher::ValueMatcherCase::kEqInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (matcher.eq_int() == values[i].mValue.int_value)) { + return true; + } + // eq_int covers both int and long. + if (values[i].mValue.getType() == LONG && + (matcher.eq_int() == values[i].mValue.long_value)) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kLteInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val <= matcher.lte_int()); - break; + return false; + case FieldValueMatcher::ValueMatcherCase::kLtInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value < matcher.lt_int())) { + return true; + } + // lt_int covers both int and long. + if (values[i].mValue.getType() == LONG && + (values[i].mValue.long_value < matcher.lt_int())) { + return true; + } } - case FieldValueMatcher::ValueMatcherCase::kGteInt: { - int64_t val = ret.first->second.has_value_int() ? - ret.first->second.value_int() : ret.first->second.value_long(); - matched = (val >= matcher.gte_int()); - break; + return false; + case FieldValueMatcher::ValueMatcherCase::kGtInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value > matcher.gt_int())) { + return true; + } + // gt_int covers both int and long. + if (values[i].mValue.getType() == LONG && + (values[i].mValue.long_value > matcher.gt_int())) { + return true; + } } - default: - break; - } - return matched; - } -} - -bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, - Field* rootField, Field* leafField) { - if (matcher.position() == Position::FIRST) { - leafField->set_position_index(0); - bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - leafField->clear_position_index(); - return res; - } else { - auto itLower = fieldMap.lower_bound(*rootField); - if (itLower == fieldMap.end()) { return false; - } - - const int leafFieldNum = leafField->field(); - leafField->set_field(leafFieldNum + 1); - auto itUpper = fieldMap.lower_bound(*rootField); - // Resets the field number. - leafField->set_field(leafFieldNum); - - switch (matcher.position()) { - case Position::LAST: - { - itUpper--; - if (itUpper == fieldMap.end()) { - return false; - } else { - int last_index = getPositionByReferenceField(*rootField, itUpper->first); - if (last_index < 0) { - return false; - } - leafField->set_position_index(last_index); - bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - leafField->clear_position_index(); - return res; - } - } - break; - case Position::ANY: - { - bool matched = false; - for (auto it = itLower; it != itUpper; ++it) { - int index = getPositionByReferenceField(*rootField, it->first); - if (index >= 0) { - leafField->set_position_index(index); - matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - leafField->clear_position_index(); - if (matched) { - break; - } - } - } - return matched; - } - default: - return false; - } - } - -} - -bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap, - const FieldValueMatcher&matcher, Field* rootField, Field* leafField) { - if (!matcher.has_position()) { - return matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); - } else { - return matchesRepeatedField(uidMap, fieldMap, matcher, rootField, leafField); + case FieldValueMatcher::ValueMatcherCase::kLtFloat: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == FLOAT && + (values[i].mValue.float_value < matcher.lt_float())) { + return true; + } + } + return false; + case FieldValueMatcher::ValueMatcherCase::kGtFloat: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == FLOAT && + (values[i].mValue.float_value > matcher.gt_float())) { + return true; + } + } + return false; + case FieldValueMatcher::ValueMatcherCase::kLteInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value <= matcher.lte_int())) { + return true; + } + // lte_int covers both int and long. + if (values[i].mValue.getType() == LONG && + (values[i].mValue.long_value <= matcher.lte_int())) { + return true; + } + } + return false; + case FieldValueMatcher::ValueMatcherCase::kGteInt: + for (int i = start; i < end; i++) { + if (values[i].mValue.getType() == INT && + (values[i].mValue.int_value >= matcher.gte_int())) { + return true; + } + // gte_int covers both int and long. + if (values[i].mValue.getType() == LONG && + (values[i].mValue.long_value >= matcher.gte_int())) { + return true; + } + } + return false; + default: + return false; } } -} // namespace - bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher, const LogEvent& event) { if (simpleMatcher.field_value_matcher_size() <= 0) { return event.GetTagId() == simpleMatcher.atom_id(); } - Field root_field; - root_field.set_field(simpleMatcher.atom_id()); - FieldValueMatcher root_field_matcher; - root_field_matcher.set_field(simpleMatcher.atom_id()); - for (int i = 0; i < simpleMatcher.field_value_matcher_size(); i++) { - *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() = - simpleMatcher.field_value_matcher(i); + for (const auto& matcher : simpleMatcher.field_value_matcher()) { + if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) { + return false; + } } - return matchFieldSimple( - uidMap, event.getFieldValueMap(), root_field_matcher, &root_field, &root_field); + return true; } -void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher, - std::vector *dimensionKeys) { - if (matcher.has_field()) { - findDimensionsValues(event.getFieldValueMap(), matcher, dimensionKeys); - } -} } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h index a45a9fb26a13500b88c3250f3169941d1e1176d0..ae946d16b352e26acd6aa9fc2e8aba52f218abfa 100644 --- a/cmds/statsd/src/matchers/matcher_util.h +++ b/cmds/statsd/src/matchers/matcher_util.h @@ -24,10 +24,10 @@ #include #include #include -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" #include "packages/UidMap.h" +#include "stats_util.h" namespace android { namespace os { @@ -45,9 +45,6 @@ bool combinationMatch(const std::vector& children, const LogicalOperation& bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper); -void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher, - std::vector *dimensionKeys); - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index ae4df3ee92f07eae50f43e51f2ed4ad4c7373de5..af2e362368c331e4669521f93c11b9e7995cff65 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -21,7 +21,6 @@ #include "guardrail/StatsdStats.h" #include "stats_util.h" #include "stats_log_util.h" -#include "dimension.h" #include #include @@ -45,8 +44,6 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_ID = 1; -const int FIELD_ID_START_REPORT_NANOS = 2; -const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_COUNT_METRICS = 5; // for CountMetricDataWrapper const int FIELD_ID_DATA = 1; @@ -55,8 +52,8 @@ const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_DIMENSION_IN_CONDITION = 2; const int FIELD_ID_BUCKET_INFO = 3; // for CountBucketInfo -const int FIELD_ID_START_BUCKET_NANOS = 1; -const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1; +const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2; const int FIELD_ID_COUNT = 3; CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric, @@ -66,21 +63,32 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket()) { - mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000; + mBucketSizeNs = + TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; } else { mBucketSizeNs = LLONG_MAX; } - // TODO: use UidMap if uid->pkg_name is required - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } + mConditionSliced = true; } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); @@ -94,40 +102,15 @@ void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } -void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - report->set_metric_id(mMetricId); - report->set_start_report_nanos(mStartTimeNs); - - auto count_metrics = report->mutable_count_metrics(); - for (const auto& counter : mPastBuckets) { - CountMetricData* metricData = count_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = - counter.first.getDimensionKeyInWhat().getDimensionsValue(); - *metricData->mutable_dimensions_in_condition() = - counter.first.getDimensionKeyInCondition().getDimensionsValue(); - for (const auto& bucket : counter.second) { - CountBucketInfo* bucketInfo = metricData->add_bucket_info(); - bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); - bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); - bucketInfo->set_count(bucket.mCount); - } - } -} - void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); if (mPastBuckets.empty()) { return; } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); - VLOG("metric %lld dump report now...",(long long)mMetricId); - for (const auto& counter : mPastBuckets) { const MetricDimensionKey& dimensionKey = counter.first; VLOG(" dimension key %s", dimensionKey.c_str()); @@ -138,25 +121,24 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionInWhatToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionInWhatToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } // Then fill bucket_info (CountBucketInfo). + for (const auto& bucket : counter.second) { long long bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketEndNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount); protoOutput->end(bucketInfoToken); @@ -167,10 +149,8 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, } protoOutput->end(protoToken); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); mPastBuckets.clear(); - mStartTimeNs = mCurrentBucketStartTimeNs; // TODO: Clear mDimensionKeyMap once the report is dumped. } @@ -205,8 +185,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { - uint64_t eventTimeNs = event.GetTimestampNs(); - + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); flushIfNeededLocked(eventTimeNs); if (condition == false) { @@ -214,13 +193,11 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( } auto it = mCurrentSlicedCounter->find(eventKey); - if (it == mCurrentSlicedCounter->end()) { // ===========GuardRail============== if (hitGuardRailLocked(eventKey)) { return; } - // create a counter for the new key (*mCurrentSlicedCounter)[eventKey] = 1; } else { @@ -228,10 +205,14 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( auto& count = it->second; count++; } - for (auto& tracker : mAnomalyTrackers) { + int64_t countWholeBucket = mCurrentSlicedCounter->find(eventKey)->second; + auto prev = mCurrentFullCounters->find(eventKey); + if (prev != mCurrentFullCounters->end()) { + countWholeBucket += prev->second; + } tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, - mCurrentSlicedCounter->find(eventKey)->second); + countWholeBucket); } VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(), @@ -241,33 +222,65 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( // When a new matched event comes in, we check if event falls into the current // bucket. If not, flush the old counter to past buckets and initialize the new bucket. void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { - if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) { + uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); + if (eventTimeNs < currentBucketEndTimeNs) { return; } + flushCurrentBucketLocked(eventTimeNs); + // Setup the bucket start time and number. + uint64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; + mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + mCurrentBucketNum += numBucketsForward; + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, + (long long)mCurrentBucketStartTimeNs); +} + +void CountMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) { + uint64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); CountBucket info; info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs; + if (eventTimeNs < fullBucketEndTimeNs) { + info.mBucketEndNs = eventTimeNs; + } else { + info.mBucketEndNs = fullBucketEndTimeNs; + } info.mBucketNum = mCurrentBucketNum; for (const auto& counter : *mCurrentSlicedCounter) { info.mCount = counter.second; auto& bucketList = mPastBuckets[counter.first]; bucketList.push_back(info); - VLOG("metric %lld, dump key value: %s -> %lld", - (long long)mMetricId, counter.first.c_str(), (long long)counter.second); + VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId, counter.first.c_str(), + (long long)counter.second); } - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum); + // If we have finished a full bucket, then send this to anomaly tracker. + if (eventTimeNs > fullBucketEndTimeNs) { + // Accumulate partial buckets with current value and then send to anomaly tracker. + if (mCurrentFullCounters->size() > 0) { + for (const auto& keyValuePair : *mCurrentSlicedCounter) { + (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second; + } + for (auto& tracker : mAnomalyTrackers) { + tracker->addPastBucket(mCurrentFullCounters, mCurrentBucketNum); + } + mCurrentFullCounters = std::make_shared(); + } else { + // Skip aggregating the partial buckets since there's no previous partial bucket. + for (auto& tracker : mAnomalyTrackers) { + tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum); + } + } + } else { + // Accumulate partial bucket. + for (const auto& keyValuePair : *mCurrentSlicedCounter) { + (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second; + } } - // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers). + // Only resets the counters, but doesn't setup the times nor numbers. + // (Do not clear since the old one is still referenced in mAnomalyTrackers). mCurrentSlicedCounter = std::make_shared(); - uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); } // Rough estimate of CountMetricProducer buffer stored. This number will be diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 8659d47735681d75716bf0523827ca9cc1aa4217..0c4291d5dfc2abf229b915cb49cfdc8c81cbdc1b 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -57,7 +57,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; @@ -71,14 +70,20 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; // Util function to flush the old packet. - void flushIfNeededLocked(const uint64_t& newEventTime); + void flushIfNeededLocked(const uint64_t& newEventTime) override; + + void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override; // TODO: Add a lock to mPastBuckets. std::unordered_map> mPastBuckets; - // The current bucket. + // The current bucket (may be a partial bucket). std::shared_ptr mCurrentSlicedCounter = std::make_shared(); + // The sum of previous partial buckets in the current full bucket (excluding the current + // partial bucket). This is only updated while flushing the current bucket. + std::shared_ptr mCurrentFullCounters = std::make_shared(); + static const size_t kBucketSize = sizeof(CountBucket{}); bool hitGuardRailLocked(const MetricDimensionKey& newKey); @@ -87,6 +92,8 @@ private: FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); + FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade); + FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index efbdae15079217b6c818d07fd62dd42c611d467f..9c65371ba958b08c7244aa941ff53e98da3659af 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -21,7 +21,6 @@ #include "guardrail/StatsdStats.h" #include "stats_util.h" #include "stats_log_util.h" -#include "dimension.h" #include #include @@ -44,8 +43,6 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_ID = 1; -const int FIELD_ID_START_REPORT_NANOS = 2; -const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_DURATION_METRICS = 6; // for DurationMetricDataWrapper const int FIELD_ID_DATA = 1; @@ -54,8 +51,8 @@ const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_DIMENSION_IN_CONDITION = 2; const int FIELD_ID_BUCKET_INFO = 3; // for DurationBucketInfo -const int FIELD_ID_START_BUCKET_NANOS = 1; -const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1; +const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2; const int FIELD_ID_DURATION = 3; DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric, @@ -70,27 +67,39 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat mStartIndex(startIndex), mStopIndex(stopIndex), mStopAllIndex(stopAllIndex), - mNested(nesting), - mInternalDimensions(internalDimensions) { + mNested(nesting) { // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract // them in the base class, because the proto generated CountMetric, and DurationMetric are // not related. Maybe we should add a template in the future?? if (metric.has_bucket()) { - mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000; + mBucketSizeNs = + TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; } else { mBucketSizeNs = LLONG_MAX; } - // TODO: use UidMap if uid->pkg_name is required - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (internalDimensions.has_field()) { + translateFieldMatcher(internalDimensions, &mInternalDimensions); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); @@ -102,12 +111,6 @@ DurationMetricProducer::~DurationMetricProducer() { sp DurationMetricProducer::addAnomalyTracker(const Alert &alert) { std::lock_guard lock(mMutex); - if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) { - ALOGW("invalid alert: threshold (%f) > possible recordable value (%d x %lld)", - alert.trigger_if_sum_gt(), alert.num_buckets(), - (long long)mBucketSizeNs); - return nullptr; - } sp anomalyTracker = new DurationAnomalyTracker(alert, mConfigKey); if (anomalyTracker != nullptr) { mAnomalyTrackers.push_back(anomalyTracker); @@ -121,13 +124,13 @@ unique_ptr DurationMetricProducer::createDurationTracker( case DurationMetric_AggregationType_SUM: return make_unique( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, - mDimensionsInCondition, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); + mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum, + mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, - mDimensionsInCondition, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); + mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum, + mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); } } @@ -142,10 +145,9 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve std::unordered_set conditionDimensionsKeySet; - ConditionState conditionState = mWizard->getMetConditionDimension( - mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet); + mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition, + &conditionDimensionsKeySet); - bool condition = (conditionState == ConditionState::kTrue); for (auto& pair : mCurrentSlicedDurationTrackerMap) { conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition()); } @@ -176,39 +178,18 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, } } -void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - report->set_metric_id(mMetricId); - report->set_start_report_nanos(mStartTimeNs); - - auto duration_metrics = report->mutable_duration_metrics(); - for (const auto& pair : mPastBuckets) { - DurationMetricData* metricData = duration_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = - pair.first.getDimensionKeyInWhat().getDimensionsValue(); - *metricData->mutable_dimensions_in_condition() = - pair.first.getDimensionKeyInCondition().getDimensionsValue(); - for (const auto& bucket : pair.second) { - auto bucketInfo = metricData->add_bucket_info(); - bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); - bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); - bucketInfo->set_duration_nanos(bucket.mDuration); - } - } -} - void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); if (mPastBuckets.empty()) { + VLOG(" Duration metric, empty return"); return; } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); - VLOG("metric %lld dump report now...", (long long)mMetricId); + VLOG("Duration metric %lld dump report now...", (long long)mMetricId); for (const auto& pair : mPastBuckets) { const MetricDimensionKey& dimensionKey = pair.first; @@ -220,15 +201,13 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -236,9 +215,9 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& bucket : pair.second) { long long bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketEndNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration); protoOutput->end(bucketInfoToken); @@ -250,19 +229,19 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, } protoOutput->end(protoToken); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); mPastBuckets.clear(); - mStartTimeNs = mCurrentBucketStartTimeNs; } -void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) { - if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { +void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { + uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); + + if (currentBucketEndTimeNs > eventTimeNs) { return; } VLOG("flushing..........."); for (auto it = mCurrentSlicedDurationTrackerMap.begin(); it != mCurrentSlicedDurationTrackerMap.end();) { - if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) { + if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) { VLOG("erase bucket for key %s", it->first.c_str()); it = mCurrentSlicedDurationTrackerMap.erase(it); } else { @@ -270,11 +249,23 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) { } } - int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; + int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; + mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; mCurrentBucketNum += numBucketsForward; } +void DurationMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) { + for (auto it = mCurrentSlicedDurationTrackerMap.begin(); + it != mCurrentSlicedDurationTrackerMap.end();) { + if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) { + VLOG("erase bucket for key %s", it->first.c_str()); + it = mCurrentSlicedDurationTrackerMap.erase(it); + } else { + ++it; + } + } +} + void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { if (mCurrentSlicedDurationTrackerMap.size() == 0) { return; @@ -313,11 +304,11 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, bool condition, const LogEvent& event) { - flushIfNeededLocked(event.GetTimestampNs()); + flushIfNeededLocked(event.GetElapsedTimestampNs()); if (matcherIndex == mStopAllIndex) { for (auto& pair : mCurrentSlicedDurationTrackerMap) { - pair.second->noteStopAll(event.GetTimestampNs()); + pair.second->noteStopAll(event.GetElapsedTimestampNs()); } return; } @@ -331,23 +322,21 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( auto it = mCurrentSlicedDurationTrackerMap.find(eventKey); - std::vector values; - getDimensionKeys(event, mInternalDimensions, &values); + std::vector values; + filterValues(mInternalDimensions, event.getValues(), &values); if (values.empty()) { if (matcherIndex == mStartIndex) { it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, - event.GetTimestampNs(), conditionKeys); + event.GetElapsedTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false); + it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetElapsedTimestampNs(), false); } } else { - for (const DimensionsValue& value : values) { + for (const auto& value : values) { if (matcherIndex == mStartIndex) { - it->second->noteStart( - HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys); + it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop( - HashableDimensionKey(value), event.GetTimestampNs(), false); + it->second->noteStop(value, event.GetElapsedTimestampNs(), false); } } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 152e570df4675d1438167e49a412646f3fe64187..5f29281a8a3c1715c5c2af18f13ea0c5959777bf 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -57,7 +57,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; @@ -73,6 +72,8 @@ private: // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime); + void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override; + const DurationMetric_AggregationType mAggregationType; // Index of the SimpleAtomMatcher which defines the start. @@ -88,7 +89,7 @@ private: const bool mNested; // The dimension from the atom predicate. e.g., uid, wakelock name. - const FieldMatcher mInternalDimensions; + vector mInternalDimensions; // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. @@ -112,6 +113,10 @@ private: FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); + FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade); + FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket); + FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade); + FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates); }; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 820d5918a92ae57344db35580bf0680d6e16033d..96d0cfcc5897861b62d6a1edf9fe90d6556e14da 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -19,6 +19,7 @@ #include "EventMetricProducer.h" #include "stats_util.h" +#include "stats_log_util.h" #include #include @@ -42,14 +43,13 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_ID = 1; -const int FIELD_ID_START_REPORT_NANOS = 2; -const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_EVENT_METRICS = 4; // for EventMetricDataWrapper const int FIELD_ID_DATA = 1; // for EventMetricData -const int FIELD_ID_TIMESTAMP_NANOS = 1; +const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1; const int FIELD_ID_ATOMS = 2; +const int FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS = 3; EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric, const int conditionIndex, @@ -57,13 +57,16 @@ EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric const uint64_t startTimeNs) : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } mConditionSliced = true; } - - startNewProtoOutputStreamLocked(); - + mProto = std::make_unique(); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -72,10 +75,6 @@ EventMetricProducer::~EventMetricProducer() { VLOG("~EventMetricProducer() called"); } -void EventMetricProducer::startNewProtoOutputStreamLocked() { - mProto = std::make_unique(); -} - void EventMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { } @@ -96,18 +95,12 @@ std::unique_ptr> serializeProtoLocked(ProtoOutputStream& pr return buffer; } -void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - -} - void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { if (mProto->size() <= 0) { return; } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); size_t bufferSize = mProto->size(); VLOG("metric %lld dump report now... proto size: %zu ", @@ -117,8 +110,7 @@ void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS, reinterpret_cast(buffer.get()->data()), buffer.get()->size()); - startNewProtoOutputStreamLocked(); - mStartTimeNs = dumpTimeNs; + mProto->clear(); } void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, @@ -137,7 +129,21 @@ void EventMetricProducer::onMatchedLogEventInternalLocked( long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs()); + const bool truncateTimestamp = + android::util::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) == + android::util::kNotTruncatingTimestampAtomWhiteList.end(); + if (truncateTimestamp) { + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, + (long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs())); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS, + (long long)truncateTimestampNsToFiveMinutes(getWallClockNs())); + } else { + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, + (long long)event.GetElapsedTimestampNs()); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS, + (long long)getWallClockNs()); + } + long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); event.ToProto(*mProto); mProto->end(eventToken); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 935f206017fa20b0a222180f58617fa35e4dfeb9..3f2c5a54e02292b142e669f8f74ddebbe2e2267c 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -40,9 +40,6 @@ public: virtual ~EventMetricProducer(); -protected: - void startNewProtoOutputStreamLocked(); - private: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, @@ -51,7 +48,6 @@ private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index d6cb1891288af35167a73cbf2074b54ffbb461ba..7d09ff9ffc8e9c8ddbbb604239df99dc2017762a 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -19,7 +19,6 @@ #include "GaugeMetricProducer.h" #include "guardrail/StatsdStats.h" -#include "dimension.h" #include "stats_log_util.h" #include @@ -45,8 +44,6 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_ID = 1; -const int FIELD_ID_START_REPORT_NANOS = 2; -const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_GAUGE_METRICS = 8; // for GaugeMetricDataWrapper const int FIELD_ID_DATA = 1; @@ -55,10 +52,10 @@ const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_DIMENSION_IN_CONDITION = 2; const int FIELD_ID_BUCKET_INFO = 3; // for GaugeBucketInfo -const int FIELD_ID_START_BUCKET_NANOS = 1; -const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1; +const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2; const int FIELD_ID_ATOM = 3; -const int FIELD_ID_TIMESTAMP = 4; +const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4; GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, @@ -72,25 +69,36 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric mCurrentSlicedBucketForAnomaly = std::make_shared(); int64_t bucketSizeMills = 0; if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket()); + bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); } else { bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); } mBucketSizeNs = bucketSizeMills * 1000000; mSamplingType = metric.sampling_type(); - mFieldFilter = metric.gauge_fields_filter(); + if (!metric.gauge_fields_filter().include_all()) { + translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers); + } // TODO: use UidMap if uid->pkg_name is required - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); // Kicks off the puller immediately. if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { @@ -117,13 +125,6 @@ GaugeMetricProducer::~GaugeMetricProducer() { } } -void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - ProtoOutputStream pbOutput; - onDumpReportLocked(dumpTimeNs, &pbOutput); - parseProtoOutputStream(pbOutput, report); -} - void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("gauge metric %lld report now...", (long long)mMetricId); @@ -134,7 +135,6 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); for (const auto& pair : mPastBuckets) { @@ -147,15 +147,13 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -163,22 +161,27 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& bucket : pair.second) { long long bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketEndNs); if (!bucket.mGaugeAtoms.empty()) { long long atomsToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ATOM); for (const auto& atom : bucket.mGaugeAtoms) { - writeFieldValueTreeToStream(*atom.mFields, protoOutput); + writeFieldValueTreeToStream(mTagId, *(atom.mFields), protoOutput); } protoOutput->end(atomsToken); - for (const auto& atom : bucket.mGaugeAtoms) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_TIMESTAMP, - (long long)atom.mTimestamps); + const bool truncateTimestamp = + android::util::kNotTruncatingTimestampAtomWhiteList.find(mTagId) == + android::util::kNotTruncatingTimestampAtomWhiteList.end(); + int64_t timestampNs = truncateTimestamp ? + truncateTimestampNsToFiveMinutes(atom.mTimestamps) : atom.mTimestamps; + protoOutput->write( + FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP, + (long long)timestampNs); } } protoOutput->end(bucketInfoToken); @@ -188,13 +191,22 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(wrapperToken); } protoOutput->end(protoToken); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); mPastBuckets.clear(); - mStartTimeNs = mCurrentBucketStartTimeNs; // TODO: Clear mDimensionKeyMap once the report is dumped. } +void GaugeMetricProducer::pullLocked() { + vector> allData; + if (!mStatsPullerManager->Pull(mPullTagId, &allData)) { + ALOGE("Stats puller failed for tag: %d", mPullTagId); + return; + } + for (const auto& data : allData) { + onMatchedLogEventLocked(0, *data); + } +} + void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) { VLOG("Metric %lld onConditionChanged", (long long)mMetricId); @@ -240,13 +252,14 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } -std::shared_ptr GaugeMetricProducer::getGaugeFields(const LogEvent& event) { - std::shared_ptr gaugeFields = - std::make_shared(event.getFieldValueMap()); - if (!mFieldFilter.include_all()) { - filterFields(mFieldFilter.fields(), gaugeFields.get()); +std::shared_ptr> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { + if (mFieldMatchers.size() > 0) { + std::shared_ptr> gaugeFields = std::make_shared>(); + filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get()); + return gaugeFields; + } else { + return std::make_shared>(event.getValues()); } - return gaugeFields; } void GaugeMetricProducer::onDataPulled(const std::vector>& allData) { @@ -285,7 +298,8 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( if (condition == false) { return; } - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); + mTagId = event.GetTagId(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); @@ -302,20 +316,18 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( if (hitGuardRailLocked(eventKey)) { return; } - GaugeAtom gaugeAtom; - gaugeAtom.mFields = getGaugeFields(event); - gaugeAtom.mTimestamps = eventTimeNs; + GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs); (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom); // Anomaly detection on gauge metric only works when there is one numeric // field specified. if (mAnomalyTrackers.size() > 0) { if (gaugeAtom.mFields->size() == 1) { - const DimensionsValue& dimensionsValue = gaugeAtom.mFields->begin()->second; + const Value& value = gaugeAtom.mFields->begin()->mValue; long gaugeVal = 0; - if (dimensionsValue.has_value_int()) { - gaugeVal = (long)dimensionsValue.value_int(); - } else if (dimensionsValue.has_value_long()) { - gaugeVal = dimensionsValue.value_long(); + if (value.getType() == INT) { + gaugeVal = (long)value.int_value; + } else if (value.getType() == LONG) { + gaugeVal = value.long_value; } for (auto& tracker : mAnomalyTrackers) { tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, @@ -326,18 +338,16 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( } void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { - mCurrentSlicedBucketForAnomaly->clear(); - status_t err = NO_ERROR; for (const auto& slice : *mCurrentSlicedBucket) { - if (slice.second.empty() || slice.second.front().mFields->empty()) { + if (slice.second.empty()) { continue; } - const DimensionsValue& dimensionsValue = slice.second.front().mFields->begin()->second; + const Value& value = slice.second.front().mFields->front().mValue; long gaugeVal = 0; - if (dimensionsValue.has_value_int()) { - gaugeVal = (long)dimensionsValue.value_int(); - } else if (dimensionsValue.has_value_long()) { - gaugeVal = dimensionsValue.value_long(); + if (value.getType() == INT) { + gaugeVal = (long)value.int_value; + } else if (value.getType() == LONG) { + gaugeVal = value.long_value; } (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal; } @@ -349,42 +359,57 @@ void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { // if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside // the GaugeMetricProducer while holding the lock. void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { - if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) { + uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); + + if (eventTimeNs < currentBucketEndTimeNs) { VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); return; } + flushCurrentBucketLocked(eventTimeNs); + + // Adjusts the bucket start and end times. + int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; + mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + mCurrentBucketNum += numBucketsForward; + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, + (long long)mCurrentBucketStartTimeNs); +} + +void GaugeMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) { + uint64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); + GaugeBucket info; info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs; + if (eventTimeNs < fullBucketEndTimeNs) { + info.mBucketEndNs = eventTimeNs; + } else { + info.mBucketEndNs = fullBucketEndTimeNs; + } info.mBucketNum = mCurrentBucketNum; for (const auto& slice : *mCurrentSlicedBucket) { info.mGaugeAtoms = slice.second; auto& bucketList = mPastBuckets[slice.first]; bucketList.push_back(info); - VLOG("gauge metric %lld, dump key value: %s", - (long long)mMetricId, slice.first.c_str()); + VLOG("gauge metric %lld, dump key value: %s", (long long)mMetricId, slice.first.c_str()); } - // Reset counters + // If we have anomaly trackers, we need to update the partial bucket values. if (mAnomalyTrackers.size() > 0) { updateCurrentSlicedBucketForAnomaly(); - for (auto& tracker : mAnomalyTrackers) { - tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum); + + if (eventTimeNs > fullBucketEndTimeNs) { + // This is known to be a full bucket, so send this data to the anomaly tracker. + for (auto& tracker : mAnomalyTrackers) { + tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum); + } + mCurrentSlicedBucketForAnomaly = std::make_shared(); } } - mCurrentSlicedBucketForAnomaly = std::make_shared(); mCurrentSlicedBucket = std::make_shared(); - - // Adjusts the bucket start time - int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); } size_t GaugeMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 86d0ccd241ebfcbbb8b99b037df9fd31837713b8..c3ae6ce921d264647a5a2f43c3fa75d3d3996dc1 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -33,7 +33,10 @@ namespace os { namespace statsd { struct GaugeAtom { - std::shared_ptr mFields; + GaugeAtom(std::shared_ptr> fields, int64_t timeNs) + : mFields(fields), mTimestamps(timeNs) { + } + std::shared_ptr> mFields; int64_t mTimestamps; }; @@ -62,6 +65,22 @@ public: // Handles when the pulled data arrives. void onDataPulled(const std::vector>& data) override; + // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. + void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) override { + std::lock_guard lock(mMutex); + + if (eventTimeNs > getCurrentBucketEndTimeNs()) { + // Flush full buckets on the normal path up to the latest bucket boundary. + flushIfNeededLocked(eventTimeNs); + } + flushCurrentBucketLocked(eventTimeNs); + mCurrentBucketStartTimeNs = eventTimeNs; + if (mPullTagId != -1) { + pullLocked(); + } + }; + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, @@ -71,7 +90,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // for testing GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric, @@ -91,7 +109,13 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; // Util function to flush the old packet. - void flushIfNeededLocked(const uint64_t& eventTime); + void flushIfNeededLocked(const uint64_t& eventTime) override; + + void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override; + + void pullLocked(); + + int mTagId; std::shared_ptr mStatsPullerManager; // tagId for pulled data. -1 if this is not pulled @@ -101,22 +125,24 @@ private: // TODO: Add a lock to mPastBuckets. std::unordered_map> mPastBuckets; - // The current bucket. + // The current partial bucket. std::shared_ptr mCurrentSlicedBucket; - // The current bucket for anomaly detection. + // The current full bucket for anomaly detection. This is updated to the latest value seen for + // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket). std::shared_ptr mCurrentSlicedBucketForAnomaly; - // Translate Atom based bucket to single numeric value bucket for anomaly + // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map + // for each slice with the latest value. void updateCurrentSlicedBucketForAnomaly(); // Whitelist of fields to report. Empty means all are reported. - FieldFilter mFieldFilter; + std::vector mFieldMatchers; GaugeMetric::SamplingType mSamplingType; // apply a whitelist on the original input - std::shared_ptr getGaugeFields(const LogEvent& event); + std::shared_ptr> getGaugeFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const MetricDimensionKey& newKey); @@ -125,6 +151,8 @@ private: FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition); FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition); + FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade); + FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade); FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection); }; diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 85e655b08f4d962882fcc3c2eb021947a1cc9245..f3307dc1d1e3789ef90f45fa6d9eb1f2e82c80ec 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "MetricProducer.h" -#include "dimension.h" +#define DEBUG true // STOPSHIP if true +#include "Log.h" +#include "MetricProducer.h" namespace android { namespace os { @@ -24,7 +25,7 @@ namespace statsd { using std::map; void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); // this is old event, maybe statsd restarted? if (eventTimeNs < mStartTimeNs) { return; @@ -35,9 +36,10 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo std::unordered_set dimensionKeysInCondition; if (mConditionSliced) { - for (const auto& link : mConditionLinks) { - getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]); + for (const auto& link : mMetric2ConditionLinks) { + getDimensionForCondition(event, link, &conditionKey[link.conditionId]); } + auto conditionState = mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, &dimensionKeysInCondition); @@ -46,20 +48,19 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo condition = mCondition; } - vector dimensionInWhatValues; - if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) { - getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues); + vector dimensionInWhatValues; + if (mDimensionsInWhat.size() > 0) { + filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues); } if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) { onMatchedLogEventInternalLocked( matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event); } else if (dimensionKeysInCondition.empty()) { - for (const DimensionsValue& whatValue : dimensionInWhatValues) { - onMatchedLogEventInternalLocked( - matcherIndex, - MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY), - conditionKey, condition, event); + for (const HashableDimensionKey& whatValue : dimensionInWhatValues) { + onMatchedLogEventInternalLocked(matcherIndex, + MetricDimensionKey(whatValue, DEFAULT_DIMENSION_KEY), + conditionKey, condition, event); } } else if (dimensionInWhatValues.empty()) { for (const auto& conditionDimensionKey : dimensionKeysInCondition) { @@ -69,12 +70,11 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo conditionKey, condition, event); } } else { - for (const DimensionsValue& whatValue : dimensionInWhatValues) { + for (const auto& whatValue : dimensionInWhatValues) { for (const auto& conditionDimensionKey : dimensionKeysInCondition) { onMatchedLogEventInternalLocked( - matcherIndex, - MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey), - conditionKey, condition, event); + matcherIndex, MetricDimensionKey(whatValue, conditionDimensionKey), + conditionKey, condition, event); } } } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 3b1498f076329d3d17cb3664816ff2819bd487a7..8663e5eed7a71907a4c12b8be2ad384da72ad0f3 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -19,6 +19,7 @@ #include +#include "HashableDimensionKey.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" @@ -53,15 +54,32 @@ public: virtual ~MetricProducer(){}; - void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override{ - // TODO: Implement me. + /** + * Forces this metric to split into a partial bucket right now. If we're past a full bucket, we + * first call the standard flushing code to flush up to the latest full bucket. Then we call + * the flush again when the end timestamp is forced to be now, and then after flushing, update + * the start timestamp to be now. + */ + void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) override { + std::lock_guard lock(mMutex); + + if (eventTimeNs > getCurrentBucketEndTimeNs()) { + // Flush full buckets on the normal path up to the latest bucket boundary. + flushIfNeededLocked(eventTimeNs); + } + // Now flush a partial bucket. + flushCurrentBucketLocked(eventTimeNs); + mCurrentBucketStartTimeNs = eventTimeNs; + // Don't update the current bucket number so that the anomaly tracker knows this bucket + // is a partial bucket and can merge it with the previous bucket. }; - void notifyAppRemoved(const string& apk, const int uid) override{ + void notifyAppRemoved(const uint64_t& eventTimeNs, const string& apk, const int uid) override{ // TODO: Implement me. }; - void onUidMapReceived() override{ + void onUidMapReceived(const uint64_t& eventTimeNs) override{ // TODO: Implement me. }; @@ -87,16 +105,12 @@ public: }; // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp. + // This method clears all the past buckets. void onDumpReport(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) { std::lock_guard lock(mMutex); return onDumpReportLocked(dumpTimeNs, protoOutput); } - void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) { - std::lock_guard lock(mMutex); - return onDumpReportLocked(dumpTimeNs, report); - } - void dumpStates(FILE* out, bool verbose) const { std::lock_guard lock(mMutex); dumpStatesLocked(out, verbose); @@ -109,6 +123,7 @@ public: return byteSizeLocked(); } + /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */ virtual sp addAnomalyTracker(const Alert &alert) { std::lock_guard lock(mMutex); sp anomalyTracker = new AnomalyTracker(alert, mConfigKey); @@ -123,6 +138,11 @@ public: return mBucketSizeNs; } + // Only needed for unit-testing to override guardrail. + void setBucketSize(int64_t bucketSize) { + mBucketSizeNs = bucketSize; + } + inline const int64_t& getMetricId() { return mMetricId; } @@ -132,19 +152,46 @@ protected: virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0; virtual void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) = 0; - virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0; virtual size_t byteSizeLocked() const = 0; virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; + /** + * Flushes the current bucket if the eventTime is after the current bucket's end time. + */ + virtual void flushIfNeededLocked(const uint64_t& eventTime){}; + + /** + * For metrics that aggregate (ie, every metric producer except for EventMetricProducer), + * we need to be able to flush the current buckets on demand (ie, end the current bucket and + * start new bucket). If this function is called when eventTimeNs is greater than the current + * bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise, + * we assume that we want to flush a partial bucket. The bucket start timestamp and bucket + * number are not changed by this function. This method should only be called by + * flushIfNeededLocked or the app upgrade handler; the caller MUST update the bucket timestamp + * and bucket number as needed. + */ + virtual void flushCurrentBucketLocked(const uint64_t& eventTimeNs){}; + + // Convenience to compute the current bucket's end time, which is always aligned with the + // start time of the metric. + uint64_t getCurrentBucketEndTimeNs() { + return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; + } + const int64_t mMetricId; const ConfigKey mConfigKey; - // The start time for the current in memory metrics data. + // The time when this metric producer was first created. The end time for the current bucket + // can be computed from this based on mCurrentBucketNum. uint64_t mStartTimeNs; + // Start time may not be aligned with the start of statsd if there is an app upgrade in the + // middle of a bucket. uint64_t mCurrentBucketStartTimeNs; + // Used by anomaly detector to track which bucket we are in. This is not sent with the produced + // report. uint64_t mCurrentBucketNum; int64_t mBucketSizeNs; @@ -157,10 +204,10 @@ protected: int mConditionTrackerIndex; - FieldMatcher mDimensionsInWhat; // The dimensions_in_what defined in statsd_config - FieldMatcher mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config + vector mDimensionsInWhat; // The dimensions_in_what defined in statsd_config + vector mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config - std::vector mConditionLinks; + std::vector mMetric2ConditionLinks; std::vector> mAnomalyTrackers; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 5f0619a21a44bbb1b445614d049b220e8f74ce75..e75b710cc9db9717890bef72a85719e27ee5c059 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -26,9 +26,11 @@ #include "matchers/SimpleLogMatchingTracker.h" #include "metrics_manager_util.h" #include "stats_util.h" +#include "stats_log_util.h" #include #include +#include using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_MESSAGE; @@ -48,9 +50,10 @@ const int FIELD_ID_METRICS = 1; MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, sp uidMap) - : mConfigKey(key), mUidMap(uidMap) { + : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(timeBaseSec * NS_PER_SEC) { mConfigValid = - initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers, + initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, + mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); @@ -89,8 +92,10 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, ALOGE("This config is too big! Reject!"); mConfigValid = false; } - - // TODO: add alert size. + if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { + ALOGE("This config has too many alerts! Reject!"); + mConfigValid = false; + } // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(), mAllConditionTrackers.size(), @@ -121,7 +126,8 @@ bool MetricsManager::isConfigValid() const { return mConfigValid; } -void MetricsManager::notifyAppUpgrade(const string& apk, const int uid, const int64_t version) { +void MetricsManager::notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) { // check if we care this package if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { return; @@ -131,7 +137,8 @@ void MetricsManager::notifyAppUpgrade(const string& apk, const int uid, const in initLogSourceWhiteList(); } -void MetricsManager::notifyAppRemoved(const string& apk, const int uid) { +void MetricsManager::notifyAppRemoved(const uint64_t& eventTimeNs, const string& apk, + const int uid) { // check if we care this package if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { return; @@ -141,21 +148,13 @@ void MetricsManager::notifyAppRemoved(const string& apk, const int uid) { initLogSourceWhiteList(); } -void MetricsManager::onUidMapReceived() { +void MetricsManager::onUidMapReceived(const uint64_t& eventTimeNs) { if (mAllowedPkg.size() == 0) { return; } initLogSourceWhiteList(); } -void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) { - for (const auto& producer : mAllMetricProducers) { - if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { - producer->onDumpReport(dumpTimeStampNs, report->add_metrics()); - } - } -} - void MetricsManager::dumpStates(FILE* out, bool verbose) { fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str()); { @@ -170,9 +169,8 @@ void MetricsManager::dumpStates(FILE* out, bool verbose) { } } -void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) { +void MetricsManager::onDumpReport(const uint64_t dumpTimeStampNs, ProtoOutputStream* protoOutput) { VLOG("=========================Metric Reports Start=========================="); - uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC; // one StatsLogReport per MetricProduer for (const auto& producer : mAllMetricProducers) { if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { @@ -182,6 +180,7 @@ void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) { protoOutput->end(token); } } + mLastReportTimeNs = dumpTimeStampNs; VLOG("=========================Metric Reports End=========================="); } @@ -191,8 +190,9 @@ void MetricsManager::onLogEvent(const LogEvent& event) { return; } - if (event.GetTagId() == android::util::APP_HOOK) { // Check that app hook fields are valid. - // TODO: Find a way to make these checks easier to maintain if the app hooks get changed. + if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { + // Check that app breadcrumb reported fields are valid. + // TODO: Find a way to make these checks easier to maintain. status_t err = NO_ERROR; // Uid is 3rd from last field and must match the caller's uid, @@ -235,7 +235,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } int tagId = event.GetTagId(); - uint64_t eventTime = event.GetTimestampNs(); + uint64_t eventTime = event.GetElapsedTimestampNs(); if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index d4b9102d5ddc73d44f039efb5e4d07a010e95741..d4f844fe386c23891ca563fdb585dc994c07275b 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -51,11 +51,12 @@ public: void setAnomalyMonitor(const sp& anomalyMonitor); - void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override; + void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) override; - void notifyAppRemoved(const string& apk, const int uid) override; + void notifyAppRemoved(const uint64_t& eventTimeNs, const string& apk, const int uid) override; - void onUidMapReceived() override; + void onUidMapReceived(const uint64_t& eventTimeNs) override; bool shouldAddUidMapListener() const { return !mAllowedPkg.empty(); @@ -63,9 +64,14 @@ public: void dumpStates(FILE* out, bool verbose); + // Returns the elapsed realtime when this metric manager last reported metrics. + uint64_t getLastReportTimeNs() { + return mLastReportTimeNs; + }; + // Config source owner can call onDumpReport() to get all the metrics collected. - virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput); - virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report); + virtual void onDumpReport(const uint64_t dumpTimeNs, + android::util::ProtoOutputStream* protoOutput); // Computes the total byte size of all metrics managed by a single config source. // Does not change the state. @@ -77,6 +83,8 @@ private: bool mConfigValid = false; + uint64_t mLastReportTimeNs; + // The uid log sources from StatsdConfig. std::vector mAllowedUid; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index c9cc7bb9c1d4528bdc86f70987227ed89cfe40b0..35fcdc48a08ad762c78ae0debfa137bd9b31beab 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -17,7 +17,6 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "dimension.h" #include "ValueMetricProducer.h" #include "guardrail/StatsdStats.h" #include "stats_log_util.h" @@ -48,8 +47,6 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_ID = 1; -const int FIELD_ID_START_REPORT_NANOS = 2; -const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_VALUE_METRICS = 7; // for ValueMetricDataWrapper const int FIELD_ID_DATA = 1; @@ -75,21 +72,34 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric // TODO: valuemetric for pushed events may need unlimited bucket length int64_t bucketSizeMills = 0; if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket()); + bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); } else { bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); } mBucketSizeNs = bucketSizeMills * 1000000; - mDimensionsInWhat = metric.dimensions_in_what(); - mDimensionsInCondition = metric.dimensions_in_condition(); + if (metric.has_dimensions_in_what()) { + translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + } + + if (metric.has_dimensions_in_condition()) { + translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition); + } if (metric.links().size() > 0) { - mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), - metric.links().end()); + for (const auto& link : metric.links()) { + Metric2Condition mc; + mc.conditionId = link.condition(); + translateFieldMatcher(link.fields_in_what(), &mc.metricFields); + translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + mMetric2ConditionLinks.push_back(mc); + } } - mConditionSliced = (metric.links().size() > 0)|| - (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); + + if (mValueField.child_size()) { + mField = mValueField.child(0).field(); + } + mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); if (!metric.has_condition() && mPullTagId != -1) { VLOG("Setting up periodic pulling for %d", mPullTagId); @@ -119,26 +129,6 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } -void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { - flushIfNeededLocked(dumpTimeNs); - report->set_metric_id(mMetricId); - report->set_start_report_nanos(mStartTimeNs); - auto value_metrics = report->mutable_value_metrics(); - for (const auto& pair : mPastBuckets) { - ValueMetricData* metricData = value_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = - pair.first.getDimensionKeyInWhat().getDimensionsValue(); - *metricData->mutable_dimensions_in_condition() = - pair.first.getDimensionKeyInCondition().getDimensionsValue(); - for (const auto& bucket : pair.second) { - ValueBucketInfo* bucketInfo = metricData->add_bucket_info(); - bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); - bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); - bucketInfo->set_value(bucket.mValue); - } - } -} - void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("metric %lld dump report now...", (long long)mMetricId); @@ -147,7 +137,6 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, return; } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); for (const auto& pair : mPastBuckets) { @@ -159,14 +148,12 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { long long dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); - writeDimensionsValueProtoToStream( - dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); } @@ -186,11 +173,9 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(wrapperToken); } protoOutput->end(protoToken); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs); VLOG("metric %lld dump report now...", (long long)mMetricId); mPastBuckets.clear(); - mStartTimeNs = mCurrentBucketStartTimeNs; // TODO: Clear mDimensionKeyMap once the report is dumped. } @@ -234,19 +219,19 @@ void ValueMetricProducer::onDataPulled(const std::vectorGetTimestampNs(); + uint64_t realEventTime = allData.at(0)->GetElapsedTimestampNs(); uint64_t eventTime = mStartTimeNs + - ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs; + ((realEventTime - mStartTimeNs) / mBucketSizeNs) * mBucketSizeNs; mCondition = false; for (const auto& data : allData) { - data->setTimestampNs(eventTime-1); + data->setElapsedTimestampNs(eventTime - 1); onMatchedLogEventLocked(0, *data); } mCondition = true; for (const auto& data : allData) { - data->setTimestampNs(eventTime); + data->setElapsedTimestampNs(eventTime); onMatchedLogEventLocked(0, *data); } } @@ -276,7 +261,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); @@ -290,11 +275,11 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( } Interval& interval = mCurrentSlicedBucket[eventKey]; - std::shared_ptr valueFieldMap = getValueFields(event); - if (valueFieldMap->empty() || valueFieldMap->size() > 1) { + int error = 0; + const long value = event.GetLong(mField, &error); + if (error < 0) { return; } - const long value = getLongFromDimenValue(valueFieldMap->begin()->second); if (mPullTagId != -1) { // for pulled events if (mCondition == true) { @@ -320,29 +305,50 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( interval.sum += value; } + long wholeBucketVal = interval.sum; + auto prev = mCurrentFullBucket.find(eventKey); + if (prev != mCurrentFullBucket.end()) { + wholeBucketVal += prev->second; + } for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, interval.sum); + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); } } -std::shared_ptr ValueMetricProducer::getValueFields(const LogEvent& event) { - std::shared_ptr valueFields = - std::make_shared(event.getFieldValueMap()); - filterFields(mValueField, valueFields.get()); - return valueFields; -} - void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { - if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) { + uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); + + if (currentBucketEndTimeNs > eventTimeNs) { VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs, - (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); + (long long)(currentBucketEndTimeNs)); return; } + + flushCurrentBucketLocked(eventTimeNs); + + int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; + mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; + mCurrentBucketNum += numBucketsForward; + + if (numBucketsForward > 1) { + VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); + } + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, + (long long)mCurrentBucketStartTimeNs); +} + +void ValueMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) { VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, (int)mCurrentSlicedBucket.size()); + uint64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); + ValueBucket info; info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs; + if (eventTimeNs < fullBucketEndTimeNs) { + info.mBucketEndNs = eventTimeNs; + } else { + info.mBucketEndNs = fullBucketEndTimeNs; + } info.mBucketNum = mCurrentBucketNum; int tainted = 0; @@ -352,27 +358,42 @@ void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { // it will auto create new vector of ValuebucketInfo if the key is not found. auto& bucketList = mPastBuckets[slice.first]; bucketList.push_back(info); + } + VLOG("%d tainted pairs in the bucket", tainted); - for (auto& tracker : mAnomalyTrackers) { - if (tracker != nullptr) { - tracker->addPastBucket(slice.first, info.mValue, info.mBucketNum); + if (eventTimeNs > fullBucketEndTimeNs) { // If full bucket, send to anomaly tracker. + // Accumulate partial buckets with current value and then send to anomaly tracker. + if (mCurrentFullBucket.size() > 0) { + for (const auto& slice : mCurrentSlicedBucket) { + mCurrentFullBucket[slice.first] += slice.second.sum; + } + for (const auto& slice : mCurrentFullBucket) { + for (auto& tracker : mAnomalyTrackers) { + if (tracker != nullptr) { + tracker->addPastBucket(slice.first, slice.second, mCurrentBucketNum); + } + } + } + mCurrentFullBucket.clear(); + } else { + // Skip aggregating the partial buckets since there's no previous partial bucket. + for (const auto& slice : mCurrentSlicedBucket) { + for (auto& tracker : mAnomalyTrackers) { + if (tracker != nullptr) { + tracker->addPastBucket(slice.first, slice.second.sum, mCurrentBucketNum); + } + } } } + } else { + // Accumulate partial bucket. + for (const auto& slice : mCurrentSlicedBucket) { + mCurrentFullBucket[slice.first] += slice.second.sum; + } } - VLOG("%d tainted pairs in the bucket", tainted); // Reset counters mCurrentSlicedBucket.clear(); - - int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; - mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - - if (numBucketsForward > 1) { - VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); - } - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); } size_t ValueMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 121ec7d185159e80bbf02ce6f832c15e3b711bdf..b518f2f841cfbfe465b5999eb69daf54d453d105 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -47,6 +47,39 @@ public: void onDataPulled(const std::vector>& data) override; + // ValueMetric needs special logic if it's a pulled atom. + void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) override { + std::lock_guard lock(mMutex); + + if (mPullTagId != -1) { + vector> allData; + mStatsPullerManager->Pull(mPullTagId, &allData); + if (allData.size() == 0) { + // This shouldn't happen since this valuemetric is not useful now. + } + + // Pretend the pulled data occurs right before the app upgrade event. + mCondition = false; + for (const auto& data : allData) { + data->setElapsedTimestampNs(eventTimeNs - 1); + onMatchedLogEventLocked(0, *data); + } + + flushCurrentBucketLocked(eventTimeNs); + mCurrentBucketStartTimeNs = eventTimeNs; + + mCondition = true; + for (const auto& data : allData) { + data->setElapsedTimestampNs(eventTimeNs); + onMatchedLogEventLocked(0, *data); + } + } else { // For pushed value metric, we simply flush and reset the current bucket start. + flushCurrentBucketLocked(eventTimeNs); + mCurrentBucketStartTimeNs = eventTimeNs; + } + }; + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, @@ -56,7 +89,6 @@ protected: private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; - void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; @@ -70,7 +102,9 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; // Util function to flush the old packet. - void flushIfNeededLocked(const uint64_t& eventTime); + void flushIfNeededLocked(const uint64_t& eventTime) override; + + void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override; const FieldMatcher mValueField; @@ -85,6 +119,8 @@ private: // tagId for pulled data. -1 if this is not pulled const int mPullTagId; + int mField; + // internal state of a bucket. typedef struct { // Pulled data always come in pair of . This holds the value @@ -101,12 +137,12 @@ private: std::unordered_map mCurrentSlicedBucket; + std::unordered_map mCurrentFullBucket; + // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. std::unordered_map> mPastBuckets; - std::shared_ptr getValueFields(const LogEvent& event); - // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const MetricDimensionKey& newKey); @@ -114,6 +150,8 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents); FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); + FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade); + FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); }; diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 45735a866978506d0e3a4a6060b5635d37a42ed4..8f236fa8bab62156a2b3568268e2dc2e445645a3 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -62,8 +62,9 @@ class DurationTracker { public: DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp wizard, int conditionIndex, - const FieldMatcher& dimensionInCondition, bool nesting, - uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, + const std::vector& dimensionInCondition, bool nesting, + uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, + uint64_t bucketSizeNs, bool conditionSliced, const std::vector>& anomalyTrackers) : mConfigKey(key), mTrackerId(id), @@ -75,7 +76,9 @@ public: mNested(nesting), mCurrentBucketStartTimeNs(currentBucketStartNs), mDuration(0), - mCurrentBucketNum(0), + mDurationFullBucket(0), + mCurrentBucketNum(currentBucketNum), + mStartTimeNs(startTimeNs), mConditionSliced(conditionSliced), mAnomalyTrackers(anomalyTrackers){}; @@ -98,6 +101,12 @@ public: uint64_t timestampNs, std::unordered_map>* output) = 0; + // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from + // an app upgrade, we assume that we're trying to form a partial bucket. + virtual bool flushCurrentBucket( + const uint64_t& eventTimeNs, + std::unordered_map>* output) = 0; + // Predict the anomaly timestamp given the current status. virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, const uint64_t currentTimestamp) const = 0; @@ -153,6 +162,13 @@ protected: } } } + + // Convenience to compute the current bucket's end time, which is always aligned with the + // start time of the metric. + uint64_t getCurrentBucketEndTimeNs() { + return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; + } + // A reference to the DurationMetricProducer's config key. const ConfigKey& mConfigKey; @@ -166,16 +182,20 @@ protected: const int64_t mBucketSizeNs; - const FieldMatcher mDimensionInCondition; + const std::vector& mDimensionInCondition; const bool mNested; uint64_t mCurrentBucketStartTimeNs; - int64_t mDuration; // current recorded duration result + int64_t mDuration; // current recorded duration result (for partial bucket) + + int64_t mDurationFullBucket; // Sum of past partial buckets in current full bucket. uint64_t mCurrentBucketNum; + const uint64_t mStartTimeNs; + const bool mConditionSliced; std::vector> mAnomalyTrackers; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index db7dea4afd04df99c5f4f2f09f7da6e12fec3287..b225560e76a8c3d35a61d6f1f2108a3ee6cbcdbe 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -27,12 +27,14 @@ namespace statsd { MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp wizard, int conditionIndex, - const FieldMatcher& dimensionInCondition, bool nesting, - uint64_t currentBucketStartNs, uint64_t bucketSizeNs, + const vector& dimensionInCondition, bool nesting, + uint64_t currentBucketStartNs, uint64_t currentBucketNum, + uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const vector>& anomalyTrackers) : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting, - currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers) { + currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs, + conditionSliced, anomalyTrackers) { } unique_ptr MaxDurationTracker::clone(const uint64_t eventTime) { @@ -53,9 +55,7 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 1. Report the tuple count if the tuple count > soft limit if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mInfos.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("MaxDurTracker %lld dropping data for dimension key %s", @@ -93,6 +93,7 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi } else { duration.state = DurationState::kStarted; duration.lastStartTime = eventTime; + startAnomalyAlarm(eventTime); } duration.startCount = 1; break; @@ -102,7 +103,6 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime, bool forceStop) { - declareAnomalyIfAlarmExpired(eventTime); VLOG("MaxDuration: key %s stop", key.c_str()); if (mInfos.find(key) == mInfos.end()) { // we didn't see a start event before. do nothing. @@ -117,12 +117,18 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ case DurationState::kStarted: { duration.startCount--; if (forceStop || !mNested || duration.startCount <= 0) { + stopAnomalyAlarm(); duration.state = DurationState::kStopped; int64_t durationTime = eventTime - duration.lastStartTime; VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime, (long long)eventTime, (long long)durationTime); - duration.lastDuration = duration.lastDuration + durationTime; + duration.lastDuration += durationTime; + if (anyStarted()) { + // In case any other dimensions are still started, we need to keep the alarm + // set. + startAnomalyAlarm(eventTime); + } VLOG(" record duration: %lld ", (long long)duration.lastDuration); } break; @@ -138,7 +144,6 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ if (duration.lastDuration > mDuration) { mDuration = duration.lastDuration; - detectAndDeclareAnomaly(eventTime, mCurrentBucketNum, mDuration); VLOG("Max: new max duration: %lld", (long long)mDuration); } // Once an atom duration ends, we erase it. Next time, if we see another atom event with the @@ -148,6 +153,15 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ } } +bool MaxDurationTracker::anyStarted() { + for (auto& pair : mInfos) { + if (pair.second.state == kStarted) { + return true; + } + } + return false; +} + void MaxDurationTracker::noteStopAll(const uint64_t eventTime) { std::set keys; for (const auto& pair : mInfos) { @@ -158,88 +172,66 @@ void MaxDurationTracker::noteStopAll(const uint64_t eventTime) { } } -bool MaxDurationTracker::flushIfNeeded( - uint64_t eventTime, unordered_map>* output) { - if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { - return false; - } - +bool MaxDurationTracker::flushCurrentBucket( + const uint64_t& eventTimeNs, + std::unordered_map>* output) { VLOG("MaxDurationTracker flushing....."); // adjust the bucket start time - int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs; - - uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs; - - DurationBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs; - info.mBucketEndNs = endTime; - info.mBucketNum = mCurrentBucketNum; - - uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs; - mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs; + int numBucketsForward = 0; + uint64_t fullBucketEnd = getCurrentBucketEndTimeNs(); + uint64_t currentBucketEndTimeNs; + if (eventTimeNs >= fullBucketEnd) { + numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs; + currentBucketEndTimeNs = fullBucketEnd; + } else { + // This must be a partial bucket. + currentBucketEndTimeNs = eventTimeNs; + } - bool hasOnGoingStartedEvent = false; // a kStarted event last across bucket boundaries. bool hasPendingEvent = false; // has either a kStarted or kPaused event across bucket boundaries - // meaning we need to carry them over to the new bucket. + // meaning we need to carry them over to the new bucket. for (auto it = mInfos.begin(); it != mInfos.end(); ++it) { - int64_t finalDuration = it->second.lastDuration; - if (it->second.state == kStarted) { - // the event is still on-going, duration needs to be updated. - // |..lastDurationTime_recorded...last_start -----|bucket_end. We need to record the - // duration between lastStartTime and bucketEnd. - int64_t durationTime = endTime - it->second.lastStartTime; - - finalDuration += durationTime; - VLOG(" unrecorded %lld -> %lld", (long long)(durationTime), (long long)finalDuration); - // if the event is still on-going, we need to fill the buckets between prev_bucket and - // now_bucket. |prev_bucket|...|..|...|now_bucket| - hasOnGoingStartedEvent = true; - } - - if (finalDuration > mDuration) { - mDuration = finalDuration; - } - if (it->second.state == DurationState::kStopped) { // No need to keep buckets for events that were stopped before. mInfos.erase(it); } else { hasPendingEvent = true; - // for kPaused, and kStarted event, we will keep track of them, and reset the start time - // and duration. - it->second.lastStartTime = mCurrentBucketStartTimeNs; - it->second.lastDuration = 0; } } + // mDuration is updated in noteStop to the maximum duration that ended in the current bucket. if (mDuration != 0) { + DurationBucket info; + info.mBucketStartNs = mCurrentBucketStartTimeNs; + info.mBucketEndNs = currentBucketEndTimeNs; + info.mBucketNum = mCurrentBucketNum; info.mDuration = mDuration; (*output)[mEventKey].push_back(info); - addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" final duration for last bucket: %lld", (long long)mDuration); } - mDuration = 0; - if (hasOnGoingStartedEvent) { - for (int i = 1; i < numBucketsForward; i++) { - DurationBucket info; - info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i; - info.mBucketEndNs = endTime + mBucketSizeNs * i; - info.mBucketNum = mCurrentBucketNum + i; - info.mDuration = mBucketSizeNs; - (*output)[mEventKey].push_back(info); - addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); - VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs); - } + if (numBucketsForward > 0) { + mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs; + mCurrentBucketNum += numBucketsForward; + } else { // We must be forming a partial bucket. + mCurrentBucketStartTimeNs = eventTimeNs; } - mCurrentBucketNum += numBucketsForward; + mDuration = 0; // If this tracker has no pending events, tell owner to remove. return !hasPendingEvent; } +bool MaxDurationTracker::flushIfNeeded( + uint64_t eventTimeNs, unordered_map>* output) { + if (eventTimeNs < getCurrentBucketEndTimeNs()) { + return false; + } + return flushCurrentBucket(eventTimeNs, output); +} + void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { // Now for each of the on-going event, check if the condition has changed for them. for (auto& pair : mInfos) { @@ -250,10 +242,11 @@ void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { ConditionState conditionState = mWizard->query( mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition, &conditionDimensionKeySet); - bool conditionMet = (conditionState == ConditionState::kTrue) && - (!mDimensionInCondition.has_field() || - conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != - conditionDimensionKeySet.end()); + bool conditionMet = + (conditionState == ConditionState::kTrue) && + (mDimensionInCondition.size() == 0 || + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != + conditionDimensionKeySet.end()); VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet); noteConditionChanged(pair.first, conditionMet, timestamp); } @@ -267,7 +260,6 @@ void MaxDurationTracker::onConditionChanged(bool condition, const uint64_t times void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, const uint64_t timestamp) { - declareAnomalyIfAlarmExpired(timestamp); auto it = mInfos.find(key); if (it == mInfos.end()) { return; @@ -275,36 +267,52 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b switch (it->second.state) { case kStarted: - // if condition becomes false, kStarted -> kPaused. Record the current duration. + // If condition becomes false, kStarted -> kPaused. Record the current duration and + // stop anomaly alarm. if (!conditionMet) { + stopAnomalyAlarm(); it->second.state = DurationState::kPaused; it->second.lastDuration += (timestamp - it->second.lastStartTime); + if (anyStarted()) { + // In case any other dimensions are still started, we need to set the alarm. + startAnomalyAlarm(timestamp); + } VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str()); } break; case kStopped: - // nothing to do if it's stopped. + // Nothing to do if it's stopped. break; case kPaused: - // if condition becomes true, kPaused -> kStarted. and the start time is the condition + // If condition becomes true, kPaused -> kStarted. and the start time is the condition // change time. if (conditionMet) { it->second.state = DurationState::kStarted; it->second.lastStartTime = timestamp; + startAnomalyAlarm(timestamp); VLOG("MaxDurationTracker Key: %s Paused->Started", key.c_str()); } break; } - if (it->second.lastDuration > mDuration) { - mDuration = it->second.lastDuration; - detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); - } + // Note that we don't update mDuration here since it's only updated during noteStop. } int64_t MaxDurationTracker::predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, const uint64_t currentTimestamp) const { - ALOGE("Max duration producer does not support anomaly timestamp prediction!!!"); - return currentTimestamp; + // The allowed time we can continue in the current state is the + // (anomaly threshold) - max(elapsed time of the started mInfos). + int64_t maxElapsed = 0; + for (auto it = mInfos.begin(); it != mInfos.end(); ++it) { + if (it->second.state == DurationState::kStarted) { + int64_t duration = + it->second.lastDuration + (currentTimestamp - it->second.lastStartTime); + if (duration > maxElapsed) { + maxElapsed = duration; + } + } + } + int64_t threshold = anomalyTracker.getAnomalyThreshold(); + return currentTimestamp + threshold - maxElapsed; } void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const { diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index 4d32a0637d56d1a0ecf639d9afb6e73d5470a253..c731b75018811b5f5a6e2bb1b88d471824292275 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -28,10 +28,11 @@ namespace statsd { // they stop or bucket expires. class MaxDurationTracker : public DurationTracker { public: - MaxDurationTracker(const ConfigKey& key, const int64_t& id, - const MetricDimensionKey& eventKey, sp wizard, - int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting, - uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, + MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, + sp wizard, int conditionIndex, + const std::vector& dimensionInCondition, bool nesting, + uint64_t currentBucketStartNs, uint64_t currentBucketNum, + uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector>& anomalyTrackers); MaxDurationTracker(const MaxDurationTracker& tracker) = default; @@ -47,6 +48,9 @@ public: bool flushIfNeeded( uint64_t timestampNs, std::unordered_map>* output) override; + bool flushCurrentBucket( + const uint64_t& eventTimeNs, + std::unordered_map>*) override; void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; @@ -56,6 +60,9 @@ public: void dumpStates(FILE* out, bool verbose) const override; private: + // Returns true if at least one of the mInfos is started. + bool anyStarted(); + std::unordered_map mInfos; void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, @@ -69,6 +76,7 @@ private: FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition); FRIEND_TEST(MaxDurationTrackerTest, TestStopAll); FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); + FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 0feae369b0d585209d9f835ed089272cfde6c957..f583f91608b8ceead7623b968c3d7bba87945e1b 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -26,12 +26,13 @@ using std::pair; OringDurationTracker::OringDurationTracker( const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp wizard, int conditionIndex, - const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, bool conditionSliced, + sp wizard, int conditionIndex, const vector& dimensionInCondition, + bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, + uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const vector>& anomalyTrackers) : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting, - currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers), + currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs, + conditionSliced, anomalyTrackers), mStarted(), mPaused() { mLastStartTime = 0; @@ -52,9 +53,7 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { } if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mConditionKeyMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey), - newTupleCount); + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("OringDurTracker %lld dropping data for dimension key %s", @@ -100,7 +99,7 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint6 } if (mStarted.empty()) { mDuration += (timestamp - mLastStartTime); - detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); + detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket); VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime, (long long)mDuration); } @@ -125,7 +124,7 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) { mDuration += (timestamp - mLastStartTime); VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime, (long long)mDuration); - detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); + detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket); } stopAnomalyAlarm(); @@ -134,51 +133,83 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) { mConditionKeyMap.clear(); } -bool OringDurationTracker::flushIfNeeded( - uint64_t eventTime, unordered_map>* output) { - if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) { - return false; - } +bool OringDurationTracker::flushCurrentBucket( + const uint64_t& eventTimeNs, + std::unordered_map>* output) { VLOG("OringDurationTracker Flushing............."); - // adjust the bucket start time - int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs; - DurationBucket current_info; - current_info.mBucketStartNs = mCurrentBucketStartTimeNs; - current_info.mBucketEndNs = current_info.mBucketStartNs + mBucketSizeNs; - current_info.mBucketNum = mCurrentBucketNum; + + // Note that we have to mimic the bucket time changes we do in the + // MetricProducer#notifyAppUpgrade. + + int numBucketsForward = 0; + uint64_t fullBucketEnd = getCurrentBucketEndTimeNs(); + uint64_t currentBucketEndTimeNs; + + if (eventTimeNs >= fullBucketEnd) { + numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs; + currentBucketEndTimeNs = fullBucketEnd; + } else { + // This must be a partial bucket. + currentBucketEndTimeNs = eventTimeNs; + } + // Process the current bucket. if (mStarted.size() > 0) { - mDuration += (current_info.mBucketEndNs - mLastStartTime); + mDuration += (currentBucketEndTimeNs - mLastStartTime); } if (mDuration > 0) { + DurationBucket current_info; + current_info.mBucketStartNs = mCurrentBucketStartTimeNs; + current_info.mBucketEndNs = currentBucketEndTimeNs; + current_info.mBucketNum = mCurrentBucketNum; current_info.mDuration = mDuration; (*output)[mEventKey].push_back(current_info); - addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum); + mDurationFullBucket += mDuration; + if (eventTimeNs > fullBucketEnd) { + // End of full bucket, can send to anomaly tracker now. + addPastBucketToAnomalyTrackers(mDurationFullBucket, current_info.mBucketNum); + mDurationFullBucket = 0; + } VLOG(" duration: %lld", (long long)current_info.mDuration); } if (mStarted.size() > 0) { for (int i = 1; i < numBucketsForward; i++) { DurationBucket info; - info.mBucketStartNs = mCurrentBucketStartTimeNs + mBucketSizeNs * i; + info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1); info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs; info.mBucketNum = mCurrentBucketNum + i; info.mDuration = mBucketSizeNs; (*output)[mEventKey].push_back(info); + // Safe to send these buckets to anomaly tracker since they must be full buckets. + // If it's a partial bucket, numBucketsForward would be 0. addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); } } - mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; - mCurrentBucketNum += numBucketsForward; - mLastStartTime = mCurrentBucketStartTimeNs; mDuration = 0; + if (numBucketsForward > 0) { + mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs; + mCurrentBucketNum += numBucketsForward; + } else { // We must be forming a partial bucket. + mCurrentBucketStartTimeNs = eventTimeNs; + } + mLastStartTime = mCurrentBucketStartTimeNs; + // if all stopped, then tell owner it's safe to remove this tracker. return mStarted.empty() && mPaused.empty(); } +bool OringDurationTracker::flushIfNeeded( + uint64_t eventTimeNs, unordered_map>* output) { + if (eventTimeNs < getCurrentBucketEndTimeNs()) { + return false; + } + return flushCurrentBucket(eventTimeNs, output); +} + void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { declareAnomalyIfAlarmExpired(timestamp); vector> startedToPaused; @@ -196,9 +227,9 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], mDimensionInCondition, &conditionDimensionKeySet); if (conditionState != ConditionState::kTrue || - (mDimensionInCondition.has_field() && + (mDimensionInCondition.size() != 0 && conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) == - conditionDimensionKeySet.end())) { + conditionDimensionKeySet.end())) { startedToPaused.push_back(*it); it = mStarted.erase(it); VLOG("Key %s started -> paused", key.c_str()); @@ -211,7 +242,7 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) mDuration += (timestamp - mLastStartTime); VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime), (long long)mDuration); - detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); + detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket); } } @@ -228,9 +259,9 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], mDimensionInCondition, &conditionDimensionKeySet); if (conditionState == ConditionState::kTrue && - (!mDimensionInCondition.has_field() || - conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) - != conditionDimensionKeySet.end())) { + (mDimensionInCondition.size() == 0 || + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != + conditionDimensionKeySet.end())) { pausedToStarted.push_back(*it); it = mPaused.erase(it); VLOG("Key %s paused -> started", key.c_str()); @@ -275,7 +306,7 @@ void OringDurationTracker::onConditionChanged(bool condition, const uint64_t tim mDuration += (timestamp - mLastStartTime); mPaused.insert(mStarted.begin(), mStarted.end()); mStarted.clear(); - detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); + detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket); } } if (mStarted.empty()) { @@ -298,7 +329,7 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs( // As we move into the future, old buckets get overwritten (so their old data is erased). // Sum of past durations. Will change as we overwrite old buckets. - int64_t pastNs = mDuration; + int64_t pastNs = mDuration + mDurationFullBucket; pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey); // How much of the threshold is still unaccounted after considering pastNs. diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 75b5a815de9cb9706b990614a5a2b39fbc473345..07c13294fa449d50e0336f9b7c13e75180165571 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -29,8 +29,9 @@ class OringDurationTracker : public DurationTracker { public: OringDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp wizard, - int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting, - uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, + int conditionIndex, const std::vector& dimensionInCondition, + bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, + uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector>& anomalyTrackers); OringDurationTracker(const OringDurationTracker& tracker) = default; @@ -46,6 +47,9 @@ public: void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; + bool flushCurrentBucket( + const uint64_t& eventTimeNs, + std::unordered_map>* output) override; bool flushIfNeeded( uint64_t timestampNs, std::unordered_map>* output) override; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index a0173ee9922f03d21b5074a9f19110d95620d311..71e5c33b1b886e16eb9286702edb5e18580effc8 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "../condition/CombinationConditionTracker.h" @@ -38,6 +38,22 @@ namespace android { namespace os { namespace statsd { +namespace { + +bool hasLeafNode(const FieldMatcher& matcher) { + if (!matcher.has_field()) { + return false; + } + for (int i = 0; i < matcher.child_size(); ++i) { + if (hasLeafNode(matcher.child(i))) { + return true; + } + } + return true; +} + +} // namespace + bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex, const bool usedForDimension, const vector>& allAtomMatchers, @@ -497,10 +513,12 @@ bool initAlerts(const StatsdConfig& config, const int metricIndex = itr->second; sp metric = allMetricProducers[metricIndex]; sp anomalyTracker = metric->addAnomalyTracker(alert); - if (anomalyTracker != nullptr) { - anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); - allAnomalyTrackers.push_back(anomalyTracker); + if (anomalyTracker == nullptr) { + // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). + return false; } + anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); + allAnomalyTrackers.push_back(anomalyTracker); } for (int i = 0; i < config.subscription_size(); ++i) { const Subscription& subscription = config.subscription(i); @@ -541,7 +559,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, ALOGE("initLogMatchingTrackers failed"); return false; } - ALOGD("initLogMatchingTrackers succeed..."); + VLOG("initLogMatchingTrackers succeed..."); if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers, trackerToConditionMap)) { diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h index df29eb0b76c42a51c23a4a5d5db53ca6962f1aa1..03cb364e83e58c381be4257360507bf5543fe74e 100644 --- a/cmds/statsd/src/packages/PackageInfoListener.h +++ b/cmds/statsd/src/packages/PackageInfoListener.h @@ -28,13 +28,15 @@ class PackageInfoListener : public virtual android::RefBase { public: // Uid map will notify this listener that the app with apk name and uid has been upgraded to // the specified version. - virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int64_t version) = 0; + virtual void notifyAppUpgrade(const uint64_t& eventTimeNs, const std::string& apk, + const int uid, const int64_t version) = 0; // Notify interested listeners that the given apk and uid combination no longer exits. - virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0; + virtual void notifyAppRemoved(const uint64_t& eventTimeNs, const std::string& apk, + const int uid) = 0; // Notify the listener that the UidMap snapshot is available. - virtual void onUidMapReceived() = 0; + virtual void onUidMapReceived(const uint64_t& eventTimeNs) = 0; }; } // namespace statsd diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 91279661b61f5d83dc09bacb6551e211a1b8994e..e322ca4bb1ac8f18d5ac4b7ef5dd9bd9e9dcabb9 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -16,6 +16,7 @@ #define DEBUG true // STOPSHIP if true #include "Log.h" +#include "stats_log_util.h" #include "guardrail/StatsdStats.h" #include "packages/UidMap.h" @@ -82,7 +83,7 @@ int64_t UidMap::getAppVersion(int uid, const string& packageName) const { void UidMap::updateMap(const vector& uid, const vector& versionCode, const vector& packageName) { - updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName); + updateMap(getElapsedRealtimeNs(), uid, versionCode, packageName); } void UidMap::updateMap(const int64_t& timestamp, const vector& uid, @@ -98,7 +99,7 @@ void UidMap::updateMap(const int64_t& timestamp, const vector& uid, } auto snapshot = mOutput.add_snapshots(); - snapshot->set_timestamp_nanos(timestamp); + snapshot->set_elapsed_timestamp_nanos(timestamp); for (size_t j = 0; j < uid.size(); j++) { auto t = snapshot->add_package_info(); t->set_name(string(String8(packageName[j]).string())); @@ -119,13 +120,13 @@ void UidMap::updateMap(const int64_t& timestamp, const vector& uid, for (auto weakPtr : broadcastList) { auto strongPtr = weakPtr.promote(); if (strongPtr != NULL) { - strongPtr->onUidMapReceived(); + strongPtr->onUidMapReceived(timestamp); } } } void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) { - updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode); + updateApp(getElapsedRealtimeNs(), app_16, uid, versionCode); } void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, @@ -137,7 +138,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i auto log = mOutput.add_changes(); log->set_deletion(false); - log->set_timestamp_nanos(timestamp); + log->set_elapsed_timestamp_nanos(timestamp); log->set_app(appName); log->set_uid(uid); log->set_version(versionCode); @@ -166,7 +167,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i for (auto weakPtr : broadcastList) { auto strongPtr = weakPtr.promote(); if (strongPtr != NULL) { - strongPtr->notifyAppUpgrade(appName, uid, versionCode); + strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode); } } } @@ -194,7 +195,7 @@ void UidMap::ensureBytesUsedBelowLimit() { } void UidMap::removeApp(const String16& app_16, const int32_t& uid) { - removeApp(time(nullptr) * NS_PER_SEC, app_16, uid); + removeApp(getElapsedRealtimeNs(), app_16, uid); } void UidMap::getListenerListCopyLocked(vector>* output) { @@ -218,7 +219,7 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i auto log = mOutput.add_changes(); log->set_deletion(true); - log->set_timestamp_nanos(timestamp); + log->set_elapsed_timestamp_nanos(timestamp); log->set_app(app); log->set_uid(uid); mBytesUsed += log->ByteSize(); @@ -239,7 +240,7 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i for (auto weakPtr : broadcastList) { auto strongPtr = weakPtr.promote(); if (strongPtr != NULL) { - strongPtr->notifyAppRemoved(app, uid); + strongPtr->notifyAppRemoved(timestamp, app, uid); } } } @@ -305,7 +306,7 @@ size_t UidMap::getBytesUsed() const { } UidMapping UidMap::getOutput(const ConfigKey& key) { - return getOutput(time(nullptr) * NS_PER_SEC, key); + return getOutput(getElapsedRealtimeNs(), key); } UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { @@ -316,13 +317,13 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { mLastUpdatePerConfigKey[key] = timestamp; int64_t newMin = getMinimumTimestampNs(); - if (newMin > prevMin) { + if (newMin > prevMin) { // Delete anything possible now that the minimum has moved forward. int64_t cutoff_nanos = newMin; auto snapshots = mOutput.mutable_snapshots(); auto it_snapshots = snapshots->cbegin(); while (it_snapshots != snapshots->cend()) { - if (it_snapshots->timestamp_nanos() < cutoff_nanos) { - // it_snapshots now points to the following element. + if (it_snapshots->elapsed_timestamp_nanos() < cutoff_nanos) { + // it_snapshots points to the following element after erasing. it_snapshots = snapshots->erase(it_snapshots); } else { ++it_snapshots; @@ -331,8 +332,8 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { auto deltas = mOutput.mutable_changes(); auto it_deltas = deltas->cbegin(); while (it_deltas != deltas->cend()) { - if (it_deltas->timestamp_nanos() < cutoff_nanos) { - // it_deltas now points to the following element. + if (it_deltas->elapsed_timestamp_nanos() < cutoff_nanos) { + // it_snapshots points to the following element after erasing. it_deltas = deltas->erase(it_deltas); } else { ++it_deltas; @@ -343,7 +344,7 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { // Produce another snapshot. This results in extra data being uploaded but helps // ensure we can re-construct the UID->app name, versionCode mapping in server. auto snapshot = mOutput.add_snapshots(); - snapshot->set_timestamp_nanos(timestamp); + snapshot->set_elapsed_timestamp_nanos(timestamp); for (auto it : mMap) { auto t = snapshot->add_package_info(); t->set_name(it.second.packageName); diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 3304f6c976aa35d08797b1da0f7c8cf7be75f24a..c41e0aaca3d229a710024e2970d27c042c4623ef 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -18,7 +18,7 @@ #include "config/ConfigKey.h" #include "config/ConfigListener.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "packages/PackageInfoListener.h" #include @@ -91,7 +91,7 @@ public: void removeIsolatedUid(int isolatedUid, int parentUid); // Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in. - int getHostUidOrSelf(int uid) const; + virtual int getHostUidOrSelf(int uid) const; // Gets the output. If every config key has received the output, then the output is cleared. UidMapping getOutput(const ConfigKey& key); diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto index dc868f997a633169f5ae76ae3c2b4f9254d686d6..56d12f8d81d460c6e79c4ae14521b3e5a1e7387a 100644 --- a/cmds/statsd/src/perfetto/perfetto_config.proto +++ b/cmds/statsd/src/perfetto/perfetto_config.proto @@ -15,7 +15,6 @@ */ syntax = "proto2"; -option optimize_for = LITE_RUNTIME; package perfetto.protos; diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index af21ca4d82e87683352b742504ef02f9c3950ed0..272e90be662da6eb9395f7e34e546f16d5ce593c 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -15,7 +15,6 @@ */ syntax = "proto2"; -option optimize_for = LITE_RUNTIME; package android.os.statsd; @@ -23,6 +22,7 @@ option java_package = "com.android.os"; option java_outer_classname = "StatsLog"; import "frameworks/base/cmds/statsd/src/atoms.proto"; +import "frameworks/base/cmds/statsd/src/stats_log_common.proto"; message DimensionsValue { optional int32 field = 1; @@ -42,15 +42,17 @@ message DimensionsValueTuple { } message EventMetricData { - optional int64 timestamp_nanos = 1; + optional int64 elapsed_timestamp_nanos = 1; optional Atom atom = 2; + + optional int64 wall_clock_timestamp_sec = 3; } message CountBucketInfo { - optional int64 start_bucket_nanos = 1; + optional int64 start_bucket_elapsed_nanos = 1; - optional int64 end_bucket_nanos = 2; + optional int64 end_bucket_elapsed_nanos = 2; optional int64 count = 3; } @@ -64,9 +66,9 @@ message CountMetricData { } message DurationBucketInfo { - optional int64 start_bucket_nanos = 1; + optional int64 start_bucket_elapsed_nanos = 1; - optional int64 end_bucket_nanos = 2; + optional int64 end_bucket_elapsed_nanos = 2; optional int64 duration_nanos = 3; } @@ -80,9 +82,9 @@ message DurationMetricData { } message ValueBucketInfo { - optional int64 start_bucket_nanos = 1; + optional int64 start_bucket_elapsed_nanos = 1; - optional int64 end_bucket_nanos = 2; + optional int64 end_bucket_elapsed_nanos = 2; optional int64 value = 3; } @@ -102,7 +104,7 @@ message GaugeBucketInfo { repeated Atom atom = 3; - repeated int64 timestamp_nanos = 4; + repeated int64 elapsed_timestamp_nanos = 4; } message GaugeMetricData { @@ -113,39 +115,10 @@ message GaugeMetricData { repeated GaugeBucketInfo bucket_info = 3; } -message UidMapping { - message PackageInfoSnapshot { - message PackageInfo { - optional string name = 1; - - optional int64 version = 2; - - optional int32 uid = 3; - } - optional int64 timestamp_nanos = 1; - - repeated PackageInfo package_info = 2; - } - repeated PackageInfoSnapshot snapshots = 1; - - message Change { - optional bool deletion = 1; - - optional int64 timestamp_nanos = 2; - optional string app = 3; - optional int32 uid = 4; - - optional int64 version = 5; - } - repeated Change changes = 2; -} - message StatsLogReport { optional int64 metric_id = 1; - optional int64 start_report_nanos = 2; - - optional int64 end_report_nanos = 3; + // Fields 2 and 3 are reserved. message EventMetricDataWrapper { repeated EventMetricData data = 1; @@ -177,6 +150,10 @@ message ConfigMetricsReport { repeated StatsLogReport metrics = 1; optional UidMapping uid_map = 2; + + optional int64 last_report_elapsed_nanos = 3; + + optional int64 current_report_elapsed_nanos = 4; } message ConfigMetricsReportList { @@ -187,87 +164,4 @@ message ConfigMetricsReportList { optional ConfigKey config_key = 1; repeated ConfigMetricsReport reports = 2; -} - -message StatsdStatsReport { - optional int32 stats_begin_time_sec = 1; - - optional int32 stats_end_time_sec = 2; - - message MatcherStats { - optional int64 id = 1; - optional int32 matched_times = 2; - } - - message ConditionStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message MetricStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message AlertStats { - optional int64 id = 1; - optional int32 alerted_times = 2; - } - - message ConfigStats { - optional int32 uid = 1; - optional int64 id = 2; - optional int32 creation_time_sec = 3; - optional int32 deletion_time_sec = 4; - optional int32 metric_count = 5; - optional int32 condition_count = 6; - optional int32 matcher_count = 7; - optional int32 alert_count = 8; - optional bool is_valid = 9; - - repeated int32 broadcast_sent_time_sec = 10; - repeated int32 data_drop_time_sec = 11; - repeated int32 dump_report_time_sec = 12; - repeated MatcherStats matcher_stats = 13; - repeated ConditionStats condition_stats = 14; - repeated MetricStats metric_stats = 15; - repeated AlertStats alert_stats = 16; - } - - repeated ConfigStats config_stats = 3; - - message AtomStats { - optional int32 tag = 1; - optional int32 count = 2; - } - - repeated AtomStats atom_stats = 7; - - message UidMapStats { - optional int32 snapshots = 1; - optional int32 changes = 2; - optional int32 bytes_used = 3; - optional int32 dropped_snapshots = 4; - optional int32 dropped_changes = 5; - } - optional UidMapStats uidmap_stats = 8; - - message AnomalyAlarmStats { - optional int32 alarms_registered = 1; - } - optional AnomalyAlarmStats anomaly_alarm_stats = 9; - - message PulledAtomStats { - optional int32 atom_id = 1; - optional int64 total_pull = 2; - optional int64 total_pull_from_cache = 3; - optional int64 min_pull_interval_sec = 4; - } - repeated PulledAtomStats pulled_atom_stats = 10; - - message LoggerErrorStats { - optional int32 logger_disconnection_sec = 1; - optional int32 error_code = 2; - } - repeated LoggerErrorStats logger_error_stats = 11; } \ No newline at end of file diff --git a/cmds/statsd/src/stats_log_common.proto b/cmds/statsd/src/stats_log_common.proto new file mode 100644 index 0000000000000000000000000000000000000000..aeecd23c65f4be1a1a773127292de54a4678db11 --- /dev/null +++ b/cmds/statsd/src/stats_log_common.proto @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 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. + */ + +syntax = "proto2"; + +package android.os.statsd; + +option java_package = "com.android.os"; +option java_outer_classname = "StatsLogCommon"; + +message UidMapping { + message PackageInfoSnapshot { + message PackageInfo { + optional string name = 1; + + optional int64 version = 2; + + optional int32 uid = 3; + } + optional int64 elapsed_timestamp_nanos = 1; + + repeated PackageInfo package_info = 2; + } + repeated PackageInfoSnapshot snapshots = 1; + + message Change { + optional bool deletion = 1; + + optional int64 elapsed_timestamp_nanos = 2; + optional string app = 3; + optional int32 uid = 4; + + optional int64 version = 5; + } + repeated Change changes = 2; +} + +message StatsdStatsReport { + optional int32 stats_begin_time_sec = 1; + + optional int32 stats_end_time_sec = 2; + + message MatcherStats { + optional int64 id = 1; + optional int32 matched_times = 2; + } + + message ConditionStats { + optional int64 id = 1; + optional int32 max_tuple_counts = 2; + } + + message MetricStats { + optional int64 id = 1; + optional int32 max_tuple_counts = 2; + } + + message AlertStats { + optional int64 id = 1; + optional int32 alerted_times = 2; + } + + message ConfigStats { + optional int32 uid = 1; + optional int64 id = 2; + optional int32 creation_time_sec = 3; + optional int32 deletion_time_sec = 4; + optional int32 metric_count = 5; + optional int32 condition_count = 6; + optional int32 matcher_count = 7; + optional int32 alert_count = 8; + optional bool is_valid = 9; + + repeated int32 broadcast_sent_time_sec = 10; + repeated int32 data_drop_time_sec = 11; + repeated int32 dump_report_time_sec = 12; + repeated MatcherStats matcher_stats = 13; + repeated ConditionStats condition_stats = 14; + repeated MetricStats metric_stats = 15; + repeated AlertStats alert_stats = 16; + } + + repeated ConfigStats config_stats = 3; + + message AtomStats { + optional int32 tag = 1; + optional int32 count = 2; + } + + repeated AtomStats atom_stats = 7; + + message UidMapStats { + optional int32 snapshots = 1; + optional int32 changes = 2; + optional int32 bytes_used = 3; + optional int32 dropped_snapshots = 4; + optional int32 dropped_changes = 5; + } + optional UidMapStats uidmap_stats = 8; + + message AnomalyAlarmStats { + optional int32 alarms_registered = 1; + } + optional AnomalyAlarmStats anomaly_alarm_stats = 9; + + message PulledAtomStats { + optional int32 atom_id = 1; + optional int64 total_pull = 2; + optional int64 total_pull_from_cache = 3; + optional int64 min_pull_interval_sec = 4; + } + repeated PulledAtomStats pulled_atom_stats = 10; + + message LoggerErrorStats { + optional int32 logger_disconnection_sec = 1; + optional int32 error_code = 2; + } + repeated LoggerErrorStats logger_error_stats = 11; +} \ No newline at end of file diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 6c6140081bd81166c25a989548d83a2c40251a95..78ebe3380b8ed1c7d412d69e11db9f5a3fda4eb8 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -16,9 +16,13 @@ #include "stats_log_util.h" +#include +#include +#include #include #include #include +#include using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; @@ -38,12 +42,11 @@ const int DIMENSIONS_VALUE_FIELD = 1; const int DIMENSIONS_VALUE_VALUE_STR = 2; const int DIMENSIONS_VALUE_VALUE_INT = 3; const int DIMENSIONS_VALUE_VALUE_LONG = 4; -const int DIMENSIONS_VALUE_VALUE_BOOL = 5; +// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type. const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; -// for MessageValue Proto -const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1; +const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; // for PulledAtomStats proto const int FIELD_ID_PULLED_ATOM_STATS = 10; @@ -51,186 +54,177 @@ const int FIELD_ID_PULL_ATOM_ID = 1; const int FIELD_ID_TOTAL_PULL = 2; const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3; const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; +namespace { -void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue, - ProtoOutputStream* protoOutput) { - if (!dimensionsValue.has_field()) { - return; - } - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field()); - switch (dimensionsValue.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, - dimensionsValue.value_str()); - break; - case DimensionsValue::ValueCase::kValueInt: - protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, - dimensionsValue.value_int()); - break; - case DimensionsValue::ValueCase::kValueLong: - protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, - dimensionsValue.value_long()); - break; - case DimensionsValue::ValueCase::kValueBool: - protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL, - dimensionsValue.value_bool()); - break; - case DimensionsValue::ValueCase::kValueFloat: - protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, - dimensionsValue.value_float()); - break; - case DimensionsValue::ValueCase::kValueTuple: - { - long long tupleToken = protoOutput->start( - FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); - for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) { - long long token = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED - | FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO); - writeDimensionsValueProtoToStream( - dimensionsValue.value_tuple().dimensions_value(i), protoOutput); - protoOutput->end(token); - } - protoOutput->end(tupleToken); +void writeDimensionToProtoHelper(const std::vector& dims, size_t* index, int depth, + int prefix, ProtoOutputStream* protoOutput) { + size_t count = dims.size(); + while (*index < count) { + const auto& dim = dims[*index]; + const int valueDepth = dim.mField.getDepth(); + const int valuePrefix = dim.mField.getPrefix(depth); + const int fieldNum = dim.mField.getPosAtDepth(depth); + if (valueDepth > 2) { + ALOGE("Depth > 2 not supported"); + return; + } + + if (depth == valueDepth && valuePrefix == prefix) { + long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + DIMENSIONS_VALUE_TUPLE_VALUE); + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); + switch (dim.mValue.getType()) { + case INT: + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, + dim.mValue.int_value); + break; + case LONG: + protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, + (long long)dim.mValue.long_value); + break; + case FLOAT: + protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, + dim.mValue.float_value); + break; + case STRING: + protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, + dim.mValue.str_value); + break; + default: + break; } - break; - default: - break; + if (token != 0) { + protoOutput->end(token); + } + (*index)++; + } else if (valueDepth > depth && valuePrefix == prefix) { + // Writing the sub tree + long long dimensionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE); + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum); + long long tupleToken = + protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); + writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), + protoOutput); + protoOutput->end(tupleToken); + protoOutput->end(dimensionToken); + } else { + // Done with the prev sub tree + return; + } } } -// for Field Proto -const int FIELD_FIELD = 1; -const int FIELD_POSITION_INDEX = 2; -const int FIELD_CHILD = 3; +} // namespace -void writeFieldProtoToStream( - const Field& field, util::ProtoOutputStream* protoOutput) { - if (!field.has_field()) { +void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStream* protoOutput) { + if (dimension.getValues().size() == 0) { return; } - protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field()); - if (field.has_position_index()) { - protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index()); - } - for (int i = 0; i < field.child_size(); ++i) { - long long childToken = protoOutput->start( - FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD); - writeFieldProtoToStream(field.child(i), protoOutput); - protoOutput->end(childToken); - } + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, + dimension.getValues()[0].mField.getTag()); + long long topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); + size_t index = 0; + writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, protoOutput); + protoOutput->end(topToken); } -namespace { - -void addOrUpdateChildrenMap( - const Field& root, - const Field& node, - std::map, FieldCmp> *childrenMap) { - Field parentNode = root; - if (node.has_position_index()) { - appendLeaf(&parentNode, node.field(), node.position_index()); - } else { - appendLeaf(&parentNode, node.field()); - } - if (childrenMap->find(parentNode) == childrenMap->end()) { - childrenMap->insert(std::make_pair(parentNode, std::set{})); - } - auto it = childrenMap->find(parentNode); - for (int i = 0; i < node.child_size(); ++i) { - auto child = node.child(i); - Field childNode = parentNode; - if (child.has_position_index()) { - appendLeaf(&childNode, child.field(), child.position_index()); - } else { - appendLeaf(&childNode, child.field()); +// Supported Atoms format +// XYZ_Atom { +// repeated SubMsg field_1 = 1; +// SubMsg2 field_2 = 2; +// int32/float/string/int63 field_3 = 3; +// } +// logd's msg format, doesn't allow us to distinguish between the 2 cases below +// Case (1): +// Atom { +// SubMsg { +// int i = 1; +// int j = 2; +// } +// repeated SubMsg +// } +// +// and case (2): +// Atom { +// SubMsg { +// repeated int i = 1; +// repeated int j = 2; +// } +// optional SubMsg = 1; +// } +// +// +void writeFieldValueTreeToStreamHelper(const std::vector& dims, size_t* index, + int depth, int prefix, ProtoOutputStream* protoOutput) { + size_t count = dims.size(); + while (*index < count) { + const auto& dim = dims[*index]; + const int valueDepth = dim.mField.getDepth(); + const int valuePrefix = dim.mField.getPrefix(depth); + const int fieldNum = dim.mField.getPosAtDepth(depth); + if (valueDepth > 2) { + ALOGE("Depth > 2 not supported"); + return; } - it->second.insert(childNode); - addOrUpdateChildrenMap(parentNode, child, childrenMap); - } -} - -void addOrUpdateChildrenMap( - const Field& field, - std::map, FieldCmp> *childrenMap) { - Field root; - addOrUpdateChildrenMap(root, field, childrenMap); -} -} // namespace - -void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap, - util::ProtoOutputStream* protoOutput) { - std::map, FieldCmp> childrenMap; - // Rebuild the field tree. - for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) { - addOrUpdateChildrenMap(it->first, &childrenMap); - } - std::stack> tokenStack; - // Iterate over the node tree to fill the Atom proto. - for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) { - const Field* nodeLeaf = getSingleLeaf(&it->first); - const int fieldNum = nodeLeaf->field(); - while (!tokenStack.empty()) { - auto currentMsgNode = tokenStack.top().second; - auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode); - if (currentMsgNodeChildrenIt->second.find(it->first) == - currentMsgNodeChildrenIt->second.end()) { - protoOutput->end(tokenStack.top().first); - tokenStack.pop(); - } else { - break; + if (depth == valueDepth && valuePrefix == prefix) { + switch (dim.mValue.getType()) { + case INT: + protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value); + break; + case LONG: + protoOutput->write(FIELD_TYPE_INT64 | fieldNum, + (long long)dim.mValue.long_value); + break; + case FLOAT: + protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value); + break; + case STRING: + protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value); + break; + default: + break; } - } - if (it->second.size() == 0) { - auto itValue = fieldValueMap.find(it->first); - if (itValue != fieldValueMap.end()) { - const DimensionsValue& value = itValue->second; - switch (value.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - protoOutput->write(FIELD_TYPE_STRING | fieldNum, - value.value_str()); - break; - case DimensionsValue::ValueCase::kValueInt: - protoOutput->write(FIELD_TYPE_INT32 | fieldNum, - value.value_int()); - break; - case DimensionsValue::ValueCase::kValueLong: - protoOutput->write(FIELD_TYPE_INT64 | fieldNum, - value.value_long()); - break; - case DimensionsValue::ValueCase::kValueBool: - protoOutput->write(FIELD_TYPE_BOOL | fieldNum, - value.value_bool()); - break; - case DimensionsValue::ValueCase::kValueFloat: - protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, - value.value_float()); - break; - // This would not happen as the node has no child. - case DimensionsValue::ValueCase::kValueTuple: - break; - default: - break; - } - } else { - ALOGE("Leaf node value not found. This should never happen."); + (*index)++; + } else if (valueDepth > depth && valuePrefix == prefix) { + // Writing the sub tree + long long msg_token = 0; + if (valueDepth == depth + 2) { + msg_token = + protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); + } else if (valueDepth == depth + 1) { + msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); } - } else { - long long token; - if (nodeLeaf->has_position_index()) { - token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); - } else { - token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); + // Directly jump to the leaf value because the repeated position field is implied + // by the position of the sub msg in the parent field. + writeFieldValueTreeToStreamHelper(dims, index, valueDepth, + dim.mField.getPrefix(valueDepth), protoOutput); + if (msg_token != 0) { + protoOutput->end(msg_token); } - tokenStack.push(std::make_pair(token, it->first)); + } else { + // Done with the prev sub tree + return; } } +} + +void writeFieldValueTreeToStream(int tagId, const std::vector& values, + util::ProtoOutputStream* protoOutput) { + long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); + + size_t index = 0; + writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput); + protoOutput->end(atomToken); +} - while (!tokenStack.empty()) { - protoOutput->end(tokenStack.top().first); - tokenStack.pop(); +int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) { + int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit); + if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) { + bucketSizeMillis = 5 * 60 * 1000LL; } + return bucketSizeMillis; } int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { @@ -274,6 +268,34 @@ void writePullerStatsToStream(const std::pair protoOutput->end(token); } +int64_t getElapsedRealtimeNs() { + return ::android::elapsedRealtimeNano(); +} + +int64_t getElapsedRealtimeSec() { + return ::android::elapsedRealtimeNano() / NS_PER_SEC; +} + +int64_t getElapsedRealtimeMillis() { + return ::android::elapsedRealtime(); +} + +int64_t getWallClockNs() { + return time(nullptr) * NS_PER_SEC; +} + +int64_t getWallClockSec() { + return time(nullptr); +} + +int64_t getWallClockMillis() { + return time(nullptr) * MS_PER_SEC; +} + +int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) { + return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60); +} + } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index cee920038eefc69ac7b19c91fceadae50b040655..c512e3c63bcfc1842c522137000e2b6f1d2d67b8 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -17,8 +17,9 @@ #pragma once #include -#include "field_util.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "FieldValue.h" +#include "HashableDimensionKey.h" +#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "guardrail/StatsdStats.h" @@ -26,20 +27,36 @@ namespace android { namespace os { namespace statsd { -// Helper function to write DimensionsValue proto to ProtoOutputStream. -void writeDimensionsValueProtoToStream(const DimensionsValue& fieldValue, - util::ProtoOutputStream* protoOutput); - -// Helper function to write Field proto to ProtoOutputStream. -void writeFieldProtoToStream(const Field& field, util::ProtoOutputStream* protoOutput); - -// Helper function to construct the field value tree and write to ProtoOutputStream -void writeFieldValueTreeToStream(const FieldValueMap& fieldValueMap, +void writeFieldValueTreeToStream(int tagId, const std::vector& values, util::ProtoOutputStream* protoOutput); +void writeDimensionToProto(const HashableDimensionKey& dimension, + util::ProtoOutputStream* protoOutput); + +// Convert the TimeUnit enum to the bucket size in millis with a guardrail on +// bucket size. +int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit); // Convert the TimeUnit enum to the bucket size in millis. int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit); +// Gets the elapsed timestamp in ns. +int64_t getElapsedRealtimeNs(); + +// Gets the elapsed timestamp in millis. +int64_t getElapsedRealtimeMillis(); + +// Gets the elapsed timestamp in seconds. +int64_t getElapsedRealtimeSec(); + +// Gets the wall clock timestamp in ns. +int64_t getWallClockNs(); + +// Gets the wall clock timestamp in millis. +int64_t getWallClockMillis(); + +// Gets the wall clock timestamp in seconds. +int64_t getWallClockSec(); + // Helper function to write PulledAtomStats to ProtoOutputStream void writePullerStatsToStream(const std::pair& pair, util::ProtoOutputStream* protoOutput); @@ -56,6 +73,9 @@ bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) { return message->ParseFromArray(pbBytes.c_str(), pbBytes.size()); } +// Returns the truncated timestamp. +int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs); + } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index 31f51a7ac0a43650878d2f85be3638d89c232dbb..c4b47dcea8a204fb5a2008e70112197f0793f187 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -18,7 +18,7 @@ #include #include "HashableDimensionKey.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "logd/LogReader.h" #include diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 3eaf7a17a3e269d6becbc8f59d2d40a4f7473998..a31385470c9fcc7fb68cd966b9e889d2abcda8d4 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -15,7 +15,6 @@ */ syntax = "proto2"; -option optimize_for = LITE_RUNTIME; package android.os.statsd; @@ -275,6 +274,12 @@ message Alarm { message IncidentdDetails { repeated int32 section = 1; + + enum Destination { + AUTOMATIC = 0; + EXPLICIT = 1; + } + optional Destination dest = 2; } message PerfettoDetails { @@ -302,6 +307,8 @@ message Subscription { PerfettoDetails perfetto_details = 5; BroadcastSubscriberDetails broadcast_subscriber_details = 6; } + + optional float probability_of_informing = 7 [default = 1.1]; } message StatsdConfig { diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 00d86582951f02bc7bbc137303115b6bf8e398a3..6a1db72b3911b57dba0b4821a5c34ccb5bbd323d 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "android-base/stringprintf.h" #include "guardrail/StatsdStats.h" #include "storage/StorageManager.h" +#include "stats_log_util.h" #include #include @@ -252,7 +253,7 @@ void StorageManager::trimToFit(const char* path) { string file_name = getFilePath(path, timestamp, uid, configID); // Check for timestamp and delete if it's too old. - long fileAge = time(nullptr) - timestamp; + long fileAge = getWallClockSec() - timestamp; if (fileAge > StatsdStats::kMaxAgeSecond) { deleteFile(file_name.c_str()); } diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9a8fc894804e945bc99e3cb68f9e59173dd3e7c --- /dev/null +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 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. + */ +#define DEBUG true +#include "Log.h" + +#include "IncidentdReporter.h" +#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" + +#include +#include +#include +#include + +namespace android { +namespace os { +namespace statsd { + +bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, + const ConfigKey& configKey) { + if (config.section_size() == 0) { + VLOG("The alert %lld contains zero section in config(%d,%lld)", alert.id(), + configKey.GetUid(), (long long) configKey.GetId()); + return false; + } + + IncidentReportArgs incidentReport; + + android::os::IncidentHeaderProto header; + header.set_alert_id(alert.id()); + header.mutable_config_key()->set_uid(configKey.GetUid()); + header.mutable_config_key()->set_id(configKey.GetId()); + incidentReport.addHeader(header); + + for (int i = 0; i < config.section_size(); i++) { + incidentReport.addSection(config.section(i)); + } + + uint8_t dest; + switch (config.dest()) { + case IncidentdDetails_Destination_AUTOMATIC: + dest = android::os::DEST_AUTOMATIC; + break; + case IncidentdDetails_Destination_EXPLICIT: + dest = android::os::DEST_EXPLICIT; + break; + default: + dest = android::os::DEST_AUTOMATIC; + } + incidentReport.setDest(dest); + + sp service = interface_cast( + defaultServiceManager()->getService(android::String16("incident"))); + if (service == nullptr) { + ALOGW("Failed to fetch incident service."); + return false; + } + VLOG("Calling incidentd %p", service.get()); + binder::Status s = service->reportIncident(incidentReport); + VLOG("Report incident status: %s", s.toString8().string()); + return s.isOk(); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h new file mode 100644 index 0000000000000000000000000000000000000000..229ed778af3aba9f1294f98545cabc46df00d172 --- /dev/null +++ b/cmds/statsd/src/subscriber/IncidentdReporter.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails + +namespace android { +namespace os { +namespace statsd { + +/** + * Calls incidentd to trigger an incident report and put in dropbox for uploading. + */ +bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, + const ConfigKey& configKey); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp index 3af684fa60697c53aae5769167a196c40a1b0fe5..95ecf8033f4c631a4a63158bca500ab9583cdc00 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp @@ -27,6 +27,8 @@ namespace android { namespace os { namespace statsd { +using std::vector; + void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId, const sp& intentSender) { @@ -98,48 +100,64 @@ void SubscriberReporter::sendBroadcastLocked(const sp& intentSender, ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService."); return; } - mStatsCompanionService->sendSubscriberBroadcast(intentSender, - configKey.GetUid(), - configKey.GetId(), - subscription.id(), - subscription.rule_id(), - protoToStatsDimensionsValue(dimKey)); + mStatsCompanionService->sendSubscriberBroadcast( + intentSender, configKey.GetUid(), configKey.GetId(), subscription.id(), + subscription.rule_id(), getStatsDimensionsValue(dimKey.getDimensionKeyInWhat())); } -StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue( - const MetricDimensionKey& dimKey) { - return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue()); +void getStatsDimensionsValueHelper(const std::vector& dims, size_t* index, int depth, + int prefix, vector* output) { + size_t count = dims.size(); + while (*index < count) { + const auto& dim = dims[*index]; + const int valueDepth = dim.mField.getDepth(); + const int valuePrefix = dim.mField.getPrefix(depth); + if (valueDepth > 2) { + ALOGE("Depth > 2 not supported"); + return; + } + if (depth == valueDepth && valuePrefix == prefix) { + switch (dim.mValue.getType()) { + case INT: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + dim.mValue.int_value)); + break; + case LONG: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + dim.mValue.long_value)); + break; + case FLOAT: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + dim.mValue.float_value)); + break; + case STRING: + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), + String16(dim.mValue.str_value.c_str()))); + break; + default: + break; + } + (*index)++; + } else if (valueDepth > depth && valuePrefix == prefix) { + vector childOutput; + getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1), + &childOutput); + output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput)); + } else { + return; + } + } } -StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue( - const DimensionsValue& protoDimsVal) { - int32_t field = protoDimsVal.field(); - - switch (protoDimsVal.value_case()) { - case DimensionsValue::ValueCase::kValueStr: - return StatsDimensionsValue(field, String16(protoDimsVal.value_str().c_str())); - case DimensionsValue::ValueCase::kValueInt: - return StatsDimensionsValue(field, static_cast(protoDimsVal.value_int())); - case DimensionsValue::ValueCase::kValueLong: - return StatsDimensionsValue(field, static_cast(protoDimsVal.value_long())); - case DimensionsValue::ValueCase::kValueBool: - return StatsDimensionsValue(field, static_cast(protoDimsVal.value_bool())); - case DimensionsValue::ValueCase::kValueFloat: - return StatsDimensionsValue(field, static_cast(protoDimsVal.value_float())); - case DimensionsValue::ValueCase::kValueTuple: - { - int sz = protoDimsVal.value_tuple().dimensions_value_size(); - std::vector sdvVec(sz); - for (int i = 0; i < sz; i++) { - sdvVec[i] = protoToStatsDimensionsValue( - protoDimsVal.value_tuple().dimensions_value(i)); - } - return StatsDimensionsValue(field, sdvVec); - } - default: - ALOGW("protoToStatsDimensionsValue failed: illegal type."); - return StatsDimensionsValue(); +StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) { + if (dim.getValues().size() == 0) { + return StatsDimensionsValue(); } + + vector fields; + size_t index = 0; + getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields); + return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields); } } // namespace statsd diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h index 13fc7fd06279c15821d427d0c873d810e69721ab..50100df56ff8de92206ed7f8231474648f412387 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ b/cmds/statsd/src/subscriber/SubscriberReporter.h @@ -21,7 +21,6 @@ #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" // DimensionsValue #include "android/os/StatsDimensionsValue.h" #include "HashableDimensionKey.h" @@ -82,6 +81,8 @@ public: const Subscription& subscription, const MetricDimensionKey& dimKey) const; + static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim); + private: SubscriberReporter() {}; @@ -102,14 +103,6 @@ private: const ConfigKey& configKey, const Subscription& subscription, const MetricDimensionKey& dimKey) const; - - /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */ - static StatsDimensionsValue protoToStatsDimensionsValue( - const DimensionsValue& protoDimsVal); - - /** Converts a HashableDimensionKey to a StatsDimensionsValue. */ - static StatsDimensionsValue protoToStatsDimensionsValue( - const MetricDimensionKey& dimKey); }; } // namespace statsd diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5846761cb8e9884de0abc5b8a73d087f400cb6e6 --- /dev/null +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2017 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. + */ +#include +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/matcher_util.h" +#include "src/logd/LogEvent.h" +#include "stats_log_util.h" +#include "stats_util.h" +#include "subscriber/SubscriberReporter.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +TEST(AtomMatcherTest, TestFieldTranslation) { + FieldMatcher matcher1; + matcher1.set_field(10); + FieldMatcher* child = matcher1.add_child(); + child->set_field(1); + child->set_position(Position::ANY); + + child = child->add_child(); + child->set_field(1); + + vector output; + translateFieldMatcher(matcher1, &output); + + EXPECT_EQ((size_t)1, output.size()); + + const auto& matcher12 = output[0]; + EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag()); + EXPECT_EQ((int32_t)0x2010001, matcher12.mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask); +} + +TEST(AtomMatcherTest, TestFilter) { + FieldMatcher matcher1; + matcher1.set_field(10); + FieldMatcher* child = matcher1.add_child(); + child->set_field(1); + child->set_position(Position::ANY); + + child = child->add_child(); + child->set_field(1); + + child = matcher1.add_child(); + child->set_field(2); + + vector matchers; + translateFieldMatcher(matcher1, &matchers); + + AttributionNodeInternal attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNodeInternal attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + AttributionNodeInternal attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("location3"); + std::vector attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3}; + + // Set up the event + LogEvent event(10, 12345); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + vector output; + + filterValues(matchers, event.getValues(), &output); + + EXPECT_EQ((size_t)(3), output.size()); + + const auto& key1 = output[0]; + EXPECT_EQ((size_t)2, key1.getValues().size()); + EXPECT_EQ((int32_t)0x02010001, key1.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)1111, key1.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x00020000, key1.getValues()[1].mField.getField()); + EXPECT_EQ("some value", key1.getValues()[1].mValue.str_value); + + const auto& key2 = output[1]; + EXPECT_EQ((size_t)2, key2.getValues().size()); + EXPECT_EQ((int32_t)0x02010001, key2.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)2222, key2.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x00020000, key2.getValues()[1].mField.getField()); + EXPECT_EQ("some value", key2.getValues()[1].mValue.str_value); + + const auto& key3 = output[2]; + EXPECT_EQ((size_t)2, key3.getValues().size()); + EXPECT_EQ((int32_t)0x02010001, key3.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)3333, key3.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x00020000, key3.getValues()[1].mField.getField()); + EXPECT_EQ("some value", key3.getValues()[1].mValue.str_value); +} + +TEST(AtomMatcherTest, TestSubDimension) { + HashableDimensionKey dim; + + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + + Value value11((int32_t)10026); + Value value22("tag2"); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + + HashableDimensionKey subDim1; + subDim1.addValue(FieldValue(field1, value1)); + + HashableDimensionKey subDim2; + subDim1.addValue(FieldValue(field2, value2)); + + EXPECT_TRUE(dim.contains(dim)); + EXPECT_TRUE(dim.contains(subDim1)); + EXPECT_TRUE(dim.contains(subDim2)); + + HashableDimensionKey subDim3; + subDim3.addValue(FieldValue(field1, value11)); + EXPECT_FALSE(dim.contains(subDim3)); + + HashableDimensionKey subDim4; + // Empty dimension is always a sub dimension of other dimensions + EXPECT_TRUE(dim.contains(subDim4)); +} + +TEST(AtomMatcherTest, TestMetric2ConditionLink) { + AttributionNodeInternal attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNodeInternal attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + AttributionNodeInternal attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("location3"); + std::vector attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3}; + + // Set up the event + LogEvent event(10, 12345); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + + FieldMatcher whatMatcher; + whatMatcher.set_field(10); + FieldMatcher* child11 = whatMatcher.add_child(); + child11->set_field(1); + child11->set_position(Position::ANY); + child11 = child11->add_child(); + child11->set_field(1); + + FieldMatcher conditionMatcher; + conditionMatcher.set_field(27); + FieldMatcher* child2 = conditionMatcher.add_child(); + child2->set_field(2); + child2->set_position(Position::LAST); + + child2 = child2->add_child(); + child2->set_field(2); + + Metric2Condition link; + + translateFieldMatcher(whatMatcher, &link.metricFields); + translateFieldMatcher(conditionMatcher, &link.conditionFields); + + EXPECT_EQ((size_t)1, link.metricFields.size()); + EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask); + EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag()); + + EXPECT_EQ((size_t)1, link.conditionFields.size()); + EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask); + EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag()); +} + +TEST(AtomMatcherTest, TestSubscriberDimensionWrite) { + HashableDimensionKey dim; + + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + Value value3((int32_t)987654); + Value value4((int32_t)99999); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + dim.addValue(FieldValue(field3, value3)); + dim.addValue(FieldValue(field4, value4)); + + SubscriberReporter::getStatsDimensionsValue(dim); + // TODO: can't test anything here because SubscriberReport class doesn't have any read api. +} + +TEST(AtomMatcherTest, TestWriteDimensionToProto) { + HashableDimensionKey dim; + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + Value value3((int32_t)987654); + Value value4((int32_t)99999); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + dim.addValue(FieldValue(field3, value3)); + dim.addValue(FieldValue(field4, value4)); + + android::util::ProtoOutputStream protoOut; + writeDimensionToProto(dim, &protoOut); + + vector outData; + outData.resize(protoOut.size()); + size_t pos = 0; + auto iter = protoOut.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + + DimensionsValue result; + EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); + EXPECT_EQ(10, result.field()); + EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case()); + EXPECT_EQ(2, result.value_tuple().dimensions_value_size()); + + const auto& dim1 = result.value_tuple().dimensions_value(0); + EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case()); + EXPECT_EQ(3, dim1.value_tuple().dimensions_value_size()); + + const auto& dim11 = dim1.value_tuple().dimensions_value(0); + EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case()); + EXPECT_EQ(10025, dim11.value_int()); + + const auto& dim12 = dim1.value_tuple().dimensions_value(1); + EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim12.value_case()); + EXPECT_EQ("tag", dim12.value_str()); + + const auto& dim13 = dim1.value_tuple().dimensions_value(2); + EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim13.value_case()); + EXPECT_EQ(987654, dim13.value_int()); + + const auto& dim2 = result.value_tuple().dimensions_value(1); + EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim2.value_case()); + EXPECT_EQ(99999, dim2.value_int()); +} + +TEST(AtomMatcherTest, TestWriteAtomToProto) { + AttributionNodeInternal attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNodeInternal attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + std::vector attribution_nodes = {attribution_node1, attribution_node2}; + + // Set up the event + LogEvent event(4, 12345); + event.write(attribution_nodes); + event.write((int32_t)999); + // Convert to a LogEvent + event.init(); + + android::util::ProtoOutputStream protoOutput; + writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput); + + vector outData; + outData.resize(protoOutput.size()); + size_t pos = 0; + auto iter = protoOutput.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + + Atom result; + EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); + EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case()); + const auto& atom = result.ble_scan_result_received(); + EXPECT_EQ(2, atom.attribution_node_size()); + EXPECT_EQ(1111, atom.attribution_node(0).uid()); + EXPECT_EQ("location1", atom.attribution_node(0).tag()); + EXPECT_EQ(2222, atom.attribution_node(1).uid()); + EXPECT_EQ("location2", atom.attribution_node(1).tag()); + EXPECT_EQ(999, atom.num_of_results()); +} + + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif \ No newline at end of file diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 111b4ba7a32c3f8db27e446631cf75d85dc87c3e..2320a9d89f6b1f4d6de7881236e1672b9c9915b5 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -14,6 +14,7 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/matcher_util.h" +#include "stats_log_util.h" #include "stats_util.h" #include @@ -35,8 +36,6 @@ const int FIELD_ID_3 = 2; const int ATTRIBUTION_UID_FIELD_ID = 1; const int ATTRIBUTION_TAG_FIELD_ID = 2; -// Private API from liblog. -extern "C" void android_log_rewind(android_log_context ctx); #ifdef __ANDROID__ TEST(AtomMatcherTest, TestSimpleMatcher) { @@ -61,19 +60,19 @@ TEST(AtomMatcherTest, TestSimpleMatcher) { TEST(AtomMatcherTest, TestAttributionMatcher) { UidMap uidMap; - AttributionNode attribution_node1; + AttributionNodeInternal attribution_node1; attribution_node1.set_uid(1111); attribution_node1.set_tag("location1"); - AttributionNode attribution_node2; + AttributionNodeInternal attribution_node2; attribution_node2.set_uid(2222); attribution_node2.set_tag("location2"); - AttributionNode attribution_node3; + AttributionNodeInternal attribution_node3; attribution_node3.set_uid(3333); attribution_node3.set_tag("location3"); - std::vector attribution_nodes = - { attribution_node1, attribution_node2, attribution_node3 }; + std::vector attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3}; // Set up the event LogEvent event(TAG_ID, 0); @@ -597,7 +596,6 @@ TEST(AtomMatcherTest, TestNorMatcher) { matcherResults.push_back(MatchingState::kMatched); EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); } - #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index fd28460e8e0151ca1b20b3e30434963d32c3af9e..2fcde29fbbdb487e6f401d29de2698835287dd9f 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -22,548 +22,142 @@ namespace android { namespace os { namespace statsd { -TEST(LogEventTest, testEmptyEvent) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - event.init(); - - DimensionsValue dimensionsValue; - EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(234, &dimensionsValue)); - FieldMatcher dimensions; - dimensions.set_field(event.GetTagId()); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); - - dimensions.add_child()->set_field(3); - dimensions.mutable_child(0)->set_position(Position::FIRST); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); - - dimensions.mutable_child(0)->set_position(Position::ANY); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); - - dimensions.mutable_child(0)->set_position(Position::LAST); - EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); +TEST(LogEventTest, TestLogParsing) { + LogEvent event1(1, 2000); + + std::vector nodes; + + AttributionNodeInternal node1; + node1.set_uid(1000); + node1.set_tag("tag1"); + nodes.push_back(node1); + + AttributionNodeInternal node2; + node2.set_uid(2000); + node2.set_tag("tag2"); + nodes.push_back(node2); + + event1.write(nodes); + event1.write("hello"); + event1.write((int32_t)10); + event1.write((int64_t)20); + event1.write((float)1.1); + event1.init(); + + const auto& items = event1.getValues(); + EXPECT_EQ((size_t)8, items.size()); + EXPECT_EQ(1, event1.GetTagId()); + + const FieldValue& item0 = event1.getValues()[0]; + EXPECT_EQ(0x2010101, item0.mField.getField()); + EXPECT_EQ(Type::INT, item0.mValue.getType()); + EXPECT_EQ(1000, item0.mValue.int_value); + + const FieldValue& item1 = event1.getValues()[1]; + EXPECT_EQ(0x2010182, item1.mField.getField()); + EXPECT_EQ(Type::STRING, item1.mValue.getType()); + EXPECT_EQ("tag1", item1.mValue.str_value); + + const FieldValue& item2 = event1.getValues()[2]; + EXPECT_EQ(0x2018201, item2.mField.getField()); + EXPECT_EQ(Type::INT, item2.mValue.getType()); + EXPECT_EQ(2000, item2.mValue.int_value); + + const FieldValue& item3 = event1.getValues()[3]; + EXPECT_EQ(0x2018282, item3.mField.getField()); + EXPECT_EQ(Type::STRING, item3.mValue.getType()); + EXPECT_EQ("tag2", item3.mValue.str_value); + + const FieldValue& item4 = event1.getValues()[4]; + EXPECT_EQ(0x20000, item4.mField.getField()); + EXPECT_EQ(Type::STRING, item4.mValue.getType()); + EXPECT_EQ("hello", item4.mValue.str_value); + + const FieldValue& item5 = event1.getValues()[5]; + EXPECT_EQ(0x30000, item5.mField.getField()); + EXPECT_EQ(Type::INT, item5.mValue.getType()); + EXPECT_EQ(10, item5.mValue.int_value); + + const FieldValue& item6 = event1.getValues()[6]; + EXPECT_EQ(0x40000, item6.mField.getField()); + EXPECT_EQ(Type::LONG, item6.mValue.getType()); + EXPECT_EQ((int64_t)20, item6.mValue.long_value); + + const FieldValue& item7 = event1.getValues()[7]; + EXPECT_EQ(0x50000, item7.mField.getField()); + EXPECT_EQ(Type::FLOAT, item7.mValue.getType()); + EXPECT_EQ((float)1.1, item7.mValue.float_value); } -TEST(LogEventTest, testRepeatedAttributionNode) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - AttributionNode attribution_node1; - attribution_node1.set_uid(1111); - attribution_node1.set_tag("locationService"); - - AttributionNode attribution_node2; - attribution_node2.set_uid(2222); - attribution_node2.set_tag("locationService2"); - - AttributionNode attribution_node3; - attribution_node3.set_uid(3333); - attribution_node3.set_tag("locationService3"); - std::vector attribution_nodes = - {attribution_node1, attribution_node2, attribution_node3}; - - // 1nd field: int32. - EXPECT_TRUE(event.write(int32_t(11))); - // 2rd field: float. - EXPECT_TRUE(event.write(3.45f)); - // Here it assume that the atom proto contains a repeated AttributionNode field. - // 3rd field: attribution node. This is repeated field. - EXPECT_TRUE(event.write(attribution_nodes)); - // 4th field: bool. - EXPECT_TRUE(event.write(true)); - // 5th field: long. - EXPECT_TRUE(event.write(uint64_t(1234))); - - event.init(); - - DimensionsValue dimensionsValue; - // Query single primitive fields. - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); - - // First attribution. - FieldMatcher first_uid_dimensions; - first_uid_dimensions.set_field(event.GetTagId()); - first_uid_dimensions.add_child()->set_field(3); - first_uid_dimensions.mutable_child(0)->set_position(Position::FIRST); - first_uid_dimensions.mutable_child(0)->add_child()->set_field(1); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_uid_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - - FieldMatcher first_tag_dimensions = first_uid_dimensions; - first_tag_dimensions.mutable_child(0)->mutable_child(0)->set_field(2); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_tag_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_str(), "locationService"); - - FieldMatcher first_attribution_dimensions = first_uid_dimensions; - first_attribution_dimensions.mutable_child(0)->add_child()->set_field(2); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_attribution_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - - FieldMatcher last_attribution_dimensions = first_attribution_dimensions; - last_attribution_dimensions.mutable_child(0)->set_position(Position::LAST); - EXPECT_TRUE(event.GetAtomDimensionsValueProto(last_attribution_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 3333); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService3"); - - FieldMatcher any_attribution_dimensions = first_attribution_dimensions; - any_attribution_dimensions.mutable_child(0)->set_position(Position::ANY); - std::vector dimensionsValues; - event.GetAtomDimensionsValueProtos(any_attribution_dimensions, &dimensionsValues); - EXPECT_EQ(dimensionsValues.size(), 3u); - EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 2222); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService2"); - EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 3333); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService3"); - - FieldMatcher mixed_dimensions = any_attribution_dimensions; - mixed_dimensions.add_child()->set_field(1000); - mixed_dimensions.add_child()->set_field(6); // missing field. - mixed_dimensions.add_child()->set_field(3); // position not set. - mixed_dimensions.add_child()->set_field(5); - mixed_dimensions.add_child()->set_field(1); - dimensionsValues.clear(); - event.GetAtomDimensionsValueProtos(mixed_dimensions, &dimensionsValues); - EXPECT_EQ(dimensionsValues.size(), 3u); - EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 3); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - 1111); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), - "locationService"); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).field(), 5); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).value_long(), long(1234)); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).field(), 1); - EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).value_int(), 11); - - EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 3); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - 2222); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), - "locationService2"); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).field(), 5); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).value_long(), long(1234)); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).field(), 1); - EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).value_int(), 11); - - EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId()); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 3); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), - 3333); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), - "locationService3"); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).field(), 5); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).value_long(), long(1234)); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).field(), 1); - EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).value_int(), 11); - - FieldMatcher wrong_dimensions = mixed_dimensions; - // Wrong tagId. - wrong_dimensions.set_field(event.GetTagId() + 100); - dimensionsValues.clear(); - event.GetAtomDimensionsValueProtos(wrong_dimensions, &dimensionsValues); - EXPECT_TRUE(dimensionsValues.empty()); +TEST(LogEventTest, TestLogParsing2) { + LogEvent event1(1, 2000); + + std::vector nodes; + + event1.write("hello"); + + // repeated msg can be in the middle + AttributionNodeInternal node1; + node1.set_uid(1000); + node1.set_tag("tag1"); + nodes.push_back(node1); + + AttributionNodeInternal node2; + node2.set_uid(2000); + node2.set_tag("tag2"); + nodes.push_back(node2); + event1.write(nodes); + + event1.write((int32_t)10); + event1.write((int64_t)20); + event1.write((float)1.1); + event1.init(); + + const auto& items = event1.getValues(); + EXPECT_EQ((size_t)8, items.size()); + EXPECT_EQ(1, event1.GetTagId()); + + const FieldValue& item = event1.getValues()[0]; + EXPECT_EQ(0x00010000, item.mField.getField()); + EXPECT_EQ(Type::STRING, item.mValue.getType()); + EXPECT_EQ("hello", item.mValue.str_value); + + const FieldValue& item0 = event1.getValues()[1]; + EXPECT_EQ(0x2020101, item0.mField.getField()); + EXPECT_EQ(Type::INT, item0.mValue.getType()); + EXPECT_EQ(1000, item0.mValue.int_value); + + const FieldValue& item1 = event1.getValues()[2]; + EXPECT_EQ(0x2020182, item1.mField.getField()); + EXPECT_EQ(Type::STRING, item1.mValue.getType()); + EXPECT_EQ("tag1", item1.mValue.str_value); + + const FieldValue& item2 = event1.getValues()[3]; + EXPECT_EQ(0x2028201, item2.mField.getField()); + EXPECT_EQ(Type::INT, item2.mValue.getType()); + EXPECT_EQ(2000, item2.mValue.int_value); + + const FieldValue& item3 = event1.getValues()[4]; + EXPECT_EQ(0x2028282, item3.mField.getField()); + EXPECT_EQ(Type::STRING, item3.mValue.getType()); + EXPECT_EQ("tag2", item3.mValue.str_value); + + const FieldValue& item5 = event1.getValues()[5]; + EXPECT_EQ(0x30000, item5.mField.getField()); + EXPECT_EQ(Type::INT, item5.mValue.getType()); + EXPECT_EQ(10, item5.mValue.int_value); + + const FieldValue& item6 = event1.getValues()[6]; + EXPECT_EQ(0x40000, item6.mField.getField()); + EXPECT_EQ(Type::LONG, item6.mValue.getType()); + EXPECT_EQ((int64_t)20, item6.mValue.long_value); + + const FieldValue& item7 = event1.getValues()[7]; + EXPECT_EQ(0x50000, item7.mField.getField()); + EXPECT_EQ(Type::FLOAT, item7.mValue.getType()); + EXPECT_EQ((float)1.1, item7.mValue.float_value); } -TEST(LogEventTest, testMessageField) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - AttributionNode attribution_node1; - attribution_node1.set_uid(1111); - attribution_node1.set_tag("locationService"); - - AttributionNode attribution_node2; - attribution_node2.set_uid(2222); - attribution_node2.set_tag("locationService2"); - - // 1nd field: int32. - EXPECT_TRUE(event.write(int32_t(11))); - // 2rd field: float. - EXPECT_TRUE(event.write(3.45f)); - // Here it assume that the atom proto contains two optional AttributionNode fields. - // 3rd field: attribution node. This is not repeated field. - EXPECT_TRUE(event.write(attribution_node1)); - // 4th field: another attribution field. This is not repeated field. - EXPECT_TRUE(event.write(attribution_node2)); - // 5th field: bool. - EXPECT_TRUE(event.write(true)); - // 6th field: long. - EXPECT_TRUE(event.write(uint64_t(1234))); - - event.init(); - - FieldMatcher uid_dimensions1; - uid_dimensions1.set_field(event.GetTagId()); - uid_dimensions1.add_child()->set_field(3); - uid_dimensions1.mutable_child(0)->add_child()->set_field(1); - - FieldMatcher tag_dimensions1; - tag_dimensions1.set_field(event.GetTagId()); - tag_dimensions1.add_child()->set_field(3); - tag_dimensions1.mutable_child(0)->add_child()->set_field(2); - - FieldMatcher attribution_dimensions1; - attribution_dimensions1.set_field(event.GetTagId()); - attribution_dimensions1.add_child()->set_field(3); - attribution_dimensions1.mutable_child(0)->add_child()->set_field(1); - attribution_dimensions1.mutable_child(0)->add_child()->set_field(2); - - FieldMatcher uid_dimensions2 = uid_dimensions1; - uid_dimensions2.mutable_child(0)->set_field(4); - - FieldMatcher tag_dimensions2 = tag_dimensions1; - tag_dimensions2.mutable_child(0)->set_field(4); - - FieldMatcher attribution_dimensions2 = attribution_dimensions1; - attribution_dimensions2.mutable_child(0)->set_field(4); - - FieldMatcher mixed_dimensions = attribution_dimensions1; - mixed_dimensions.add_child()->set_field(4); - mixed_dimensions.mutable_child(1)->add_child()->set_field(1); - mixed_dimensions.add_child()->set_field(1000); - mixed_dimensions.add_child()->set_field(5); - mixed_dimensions.add_child()->set_field(1); - - DimensionsValue dimensionsValue; - - // Query single primitive fields. - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); - - // Query atom field 3: attribution node uid field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - - // Query atom field 3: attribution node tag field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_str(), "locationService"); - - // Query atom field 3: attribution node uid + tag fields. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - - // Query atom field 4: attribution node uid field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 2222); - - // Query atom field 4: attribution node tag field only. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_str(), "locationService2"); - - // Query atom field 4: attribution node uid + tag fields. - EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 2222); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService2"); - - // Query multiple fields: - // 1/ Field 3: attribution uid + tag. - // 2/ Field 4: attribution uid only. - // 3/ Field not exist. - // 4/ Primitive fields #5 - // 5/ Primitive fields #1 - EXPECT_TRUE(event.GetAtomDimensionsValueProto(mixed_dimensions, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value_size(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(0).value_int(), 1111); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() - .dimensions_value(1).value_str(), "locationService"); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).field(), 4); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() - .dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() - .dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() - .dimensions_value(0).value_int(), 2222); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).field(), 5); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).value_int(), true); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).value_int(), 11); -} - -TEST(LogEventTest, testAllPrimitiveFields) { - const int32_t TAG_ID = 123; - LogEvent event(TAG_ID, 0); - - // 1nd field: int32. - EXPECT_TRUE(event.write(int32_t(11))); - // 2rd field: float. - EXPECT_TRUE(event.write(3.45f)); - // 3th field: string. - EXPECT_TRUE(event.write("test")); - // 4th field: bool. - EXPECT_TRUE(event.write(true)); - // 5th field: bool. - EXPECT_TRUE(event.write(false)); - // 6th field: long. - EXPECT_TRUE(event.write(uint64_t(1234))); - - event.init(); - - DimensionsValue dimensionsValue; - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(3, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_str(), "test"); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); - // The bool value is stored in value_int field as logD does not support bool. - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), false); - - EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue)); - EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6); - EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); - - // Field not exist. - EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(7, &dimensionsValue)); -} - -TEST(LogEventTest, testWriteAtomProtoToStream) { - AttributionNode attribution_node1; - attribution_node1.set_uid(1111); - attribution_node1.set_tag("locationService"); - - AttributionNode attribution_node2; - attribution_node2.set_uid(2222); - attribution_node2.set_tag("locationService2"); - - AttributionNode attribution_node3; - attribution_node3.set_uid(3333); - attribution_node3.set_tag("locationService3"); - std::vector attribution_nodes = - {attribution_node1, attribution_node2, attribution_node3}; - - LogEvent event(1, 0); - EXPECT_TRUE(event.write("222")); - EXPECT_TRUE(event.write(attribution_nodes)); - EXPECT_TRUE(event.write(345)); - EXPECT_TRUE(event.write(attribution_node3)); - EXPECT_TRUE(event.write("hello")); - event.init(); - - util::ProtoOutputStream protoOutput; - // For now only see whether it will crash. - // TODO(yanglu): test parsing from stream. - event.ToProto(protoOutput); -} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index aab5bedb3cbeb93e7886a5f99f5733b02ccbe248..cb72697941e0c4f340f1faaeeedba573ff5a3200 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -45,7 +45,7 @@ public: } MOCK_METHOD0(byteSize, size_t()); - MOCK_METHOD1(onDumpReport, void(ProtoOutputStream* output)); + MOCK_METHOD2(onDumpReport, void(const uint64_t timeNs, ProtoOutputStream* output)); }; TEST(StatsLogProcessorTest, TestRateLimitByteSize) { @@ -69,24 +69,26 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { sp m = new UidMap(); sp anomalyMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, 0, - [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); + StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) { + broadcastCount++; + }); MockMetricsManager mockMetricsManager; ConfigKey key(100, 12345); EXPECT_CALL(mockMetricsManager, byteSize()) - .Times(2) + .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); // Expect only one broadcast despite always returning a size that should trigger broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); EXPECT_EQ(1, broadcastCount); + // b/73089712 // This next call to flush should not trigger a broadcast. - p.mLastByteSizeTimes.clear(); // Force another check for byte size. - p.flushIfNecessaryLocked(2, key, mockMetricsManager); - EXPECT_EQ(1, broadcastCount); + // p.mLastByteSizeTimes.clear(); // Force another check for byte size. + // p.flushIfNecessaryLocked(2, key, mockMetricsManager); + // EXPECT_EQ(1, broadcastCount); } TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { @@ -103,7 +105,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); - EXPECT_CALL(mockMetricsManager, onDumpReport(_)).Times(1); + EXPECT_CALL(mockMetricsManager, onDumpReport(_, _)).Times(1); // Expect to call the onDumpReport and skip the broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index a415ea1a3069a659973bb3b3b09559ab7e4f0a42..b4a7bb706029437688cecd789bd1b5d40ee72783 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -34,10 +34,10 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); MetricDimensionKey getMockMetricDimensionKey(int key, string value) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(key); - dimensionsValue.set_value_str(value); - return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY); + int pos[] = {key, 0, 0}; + HashableDimensionKey dim; + dim.addValue(FieldValue(Field(1, pos, 0), Value(value))); + return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY); } void AddValueToBucket(const std::vector>& key_value_pair_list, diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index d1b7b2842fd2a51d04aa8d9267ce2035cb3a423d..3dc3fd1160f19e55d7db2d50156b79d8c5710142 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -58,9 +58,9 @@ SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, } void writeAttributionNodesToEvent(LogEvent* event, const std::vector &uids) { - std::vector nodes; + std::vector nodes; for (size_t i = 0; i < uids.size(); ++i) { - AttributionNode node; + AttributionNodeInternal node; node.set_uid(uids[i]); nodes.push_back(node); } @@ -80,31 +80,31 @@ std::map> getWakeLockQueryKey( const std::vector &uids, const string& conditionName) { std::map> outputKeyMap; std::vector uid_indexes; + int pos[] = {1, 1, 1}; + int depth = 2; + Field field(1, pos, depth); switch(position) { case Position::FIRST: uid_indexes.push_back(0); break; case Position::LAST: uid_indexes.push_back(uids.size() - 1); + field.setField(0x02018001); break; case Position::ANY: uid_indexes.resize(uids.size()); std::iota(uid_indexes.begin(), uid_indexes.end(), 0); + field.setField(0x02010001); break; default: break; } for (const int idx : uid_indexes) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(TAG_ID); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field( - ATTRIBUTION_NODE_FIELD_ID); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) - ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) - ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]); - outputKeyMap[StringToId(conditionName)].push_back(HashableDimensionKey(dimensionsValue)); + Value value((int32_t)uids[idx]); + HashableDimensionKey dim; + dim.addValue(FieldValue(field, value)); + outputKeyMap[StringToId(conditionName)].push_back(dim); } return outputKeyMap; } @@ -265,7 +265,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { TEST(SimpleConditionTrackerTest, TestSlicedCondition) { for (Position position : { Position::ANY, Position::FIRST, Position::LAST}) { - FieldMatcher dimensionInCondition; + vector dimensionInCondition; std::unordered_set dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( @@ -374,7 +374,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { - FieldMatcher dimensionInCondition; + vector dimensionInCondition; std::unordered_set dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( @@ -470,7 +470,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { TEST(SimpleConditionTrackerTest, TestStopAll) { for (Position position : {Position::ANY, Position::FIRST, Position::LAST}) { - FieldMatcher dimensionInCondition; + vector dimensionInCondition; std::unordered_set dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, @@ -576,7 +576,6 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } - } } // namespace statsd diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp deleted file mode 100644 index 678abaed5fd85a0a18c229bba99f599476b698ed..0000000000000000000000000000000000000000 --- a/cmds/statsd/tests/dimension_test.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (C) 2017 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. - -#include "dimension.h" - -#include - -using namespace android::os::statsd; - -#ifdef __ANDROID__ - -TEST(DimensionTest, subLeafNodes) { - DimensionsValue dimension; - int tagId = 100; - dimension.set_field(tagId); - auto child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(1); - child->set_value_int(2000); - - child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(3); - child->set_value_str("test"); - - child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(4); - auto grandChild = child->mutable_value_tuple()->add_dimensions_value(); - grandChild->set_field(1); - grandChild->set_value_float(1.3f); - grandChild = child->mutable_value_tuple()->add_dimensions_value(); - grandChild->set_field(3); - grandChild->set_value_str("tag"); - - child = dimension.mutable_value_tuple()->add_dimensions_value(); - child->set_field(6); - child->set_value_bool(false); - - DimensionsValue sub_dimension; - FieldMatcher matcher; - - // Tag id not matched. - matcher.set_field(tagId + 1); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field not exist. - matcher.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(5); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field exists. - matcher.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(3); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field exists. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(6); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Field exists. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(1); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Not leaf field. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - matcher.add_child()->set_field(4); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Grand-child leaf field not exist. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - auto childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(2); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Grand-child leaf field. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(1); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Multiple grand-child fields. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - childMatcher->add_child()->set_field(1); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Multiple fields. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - childMatcher->add_child()->set_field(1); - matcher.add_child()->set_field(3); - EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); - - // Subset of the fields not exist. - matcher.Clear(); - sub_dimension.Clear(); - matcher.set_field(tagId); - childMatcher = matcher.add_child(); - childMatcher->set_field(4); - childMatcher->add_child()->set_field(3); - childMatcher->add_child()->set_field(1); - matcher.add_child()->set_field(2); - EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); -} - -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp index a56db28e64b39af3b12968a07766957765330053..7a7e000fb98ff5686ffbbe33a99e7a61e55d0057 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -47,7 +47,7 @@ StatsdConfig CreateStatsdConfig() { *countMetric->mutable_dimensions_in_what() = CreateAttributionUidAndTagDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - countMetric->set_bucket(ONE_MINUTE); + countMetric->set_bucket(FIVE_MINUTES); return config; } @@ -75,41 +75,40 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { android::String16("APP3"), 333 /* uid */, 2 /* version code*/); // GMS core node is in the middle. - std::vector attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(333, "App3")}; + std::vector attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(333, "App3")}; // GMS core node is the last one. - std::vector attributions2 = - {CreateAttribution(111, "App1"), CreateAttribution(333, "App3"), - CreateAttribution(222, "GMSCoreModule1")}; + std::vector attributions2 = {CreateAttribution(111, "App1"), + CreateAttribution(333, "App3"), + CreateAttribution(222, "GMSCoreModule1")}; // GMS core node is the first one. - std::vector attributions3 = - {CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")}; + std::vector attributions3 = {CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(333, "App3")}; // Single GMS core node. - std::vector attributions4 = - {CreateAttribution(222, "GMSCoreModule1")}; + std::vector attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; // GMS core has another uid. - std::vector attributions5 = - {CreateAttribution(111, "App1"), CreateAttribution(444, "GMSCoreModule2"), - CreateAttribution(333, "App3")}; + std::vector attributions5 = {CreateAttribution(111, "App1"), + CreateAttribution(444, "GMSCoreModule2"), + CreateAttribution(333, "App3")}; // Multiple GMS core nodes. - std::vector attributions6 = - {CreateAttribution(444, "GMSCoreModule2"), CreateAttribution(222, "GMSCoreModule1")}; + std::vector attributions6 = {CreateAttribution(444, "GMSCoreModule2"), + CreateAttribution(222, "GMSCoreModule1")}; // No GMS core nodes. - std::vector attributions7 = - {CreateAttribution(111, "App1"), CreateAttribution(333, "App3")}; - std::vector attributions8 = {CreateAttribution(111, "App1")}; + std::vector attributions7 = {CreateAttribution(111, "App1"), + CreateAttribution(333, "App3")}; + std::vector attributions8 = {CreateAttribution(111, "App1")}; // GMS core node with isolated uid. const int isolatedUid = 666; - std::vector attributions9 = - {CreateAttribution(isolatedUid, "GMSCoreModule3")}; + std::vector attributions9 = { + CreateAttribution(isolatedUid, "GMSCoreModule3")}; std::vector> events; // Events 1~4 are in the 1st bucket. @@ -146,9 +145,13 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); + StatsLogReport::CountMetricDataWrapper countMetrics; sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); EXPECT_EQ(countMetrics.data_size(), 4); @@ -159,11 +162,11 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); data = countMetrics.data(1); ValidateAttributionUidAndTagDimension( @@ -171,11 +174,11 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "GMSCoreModule1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = countMetrics.data(2); ValidateAttributionUidAndTagDimension( @@ -183,8 +186,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "GMSCoreModule3"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); data = countMetrics.data(3); ValidateAttributionUidAndTagDimension( @@ -192,8 +195,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "GMSCoreModule2"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); } #else @@ -202,4 +205,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp index b5d48efb55f69d126696af55c4b4049540534a05..01348bd664aa81c0bc4f1a237d0c81251d3babdd 100644 --- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp @@ -43,8 +43,8 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() { auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); // The predicate is dimensioning by any attribution node and both by uid and tag. *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateAttributionUidAndTagDimensions( - android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + CreateAttributionUidAndTagDimensions(android::util::WAKELOCK_STATE_CHANGED, + {Position::FIRST}); *config.add_predicate() = holdingWakelockPredicate; auto combinationPredicate = config.add_predicate(); @@ -57,11 +57,11 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() { metric->set_id(StringToId("ScreenBrightnessChangeMetric")); metric->set_what(screenBrightnessChangeAtomMatcher.id()); metric->set_condition(combinationPredicate->id()); - *metric->mutable_dimensions_in_what() = CreateDimensions( - android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */}); + *metric->mutable_dimensions_in_what() = + CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); return config; } @@ -72,62 +72,54 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { auto config = CreateCountMetricWithNoLinkConfig(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector attributions2 = {CreateAttribution(333, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector> events; - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10)); - - events.push_back(CreateAcquireWakelockEvent( - attributions1, "wl1", bucketStartTimeNs + 200)); - events.push_back(CreateReleaseWakelockEvent( - attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1)); - - events.push_back(CreateAcquireWakelockEvent( - attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100)); - events.push_back(CreateReleaseWakelockEvent( - attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50)); - - events.push_back(CreateScreenBrightnessChangedEvent( - 123, bucketStartTimeNs + 11)); - events.push_back(CreateScreenBrightnessChangedEvent( - 123, bucketStartTimeNs + 101)); - events.push_back(CreateScreenBrightnessChangedEvent( - 123, bucketStartTimeNs + 201)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + 203)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + bucketSizeNs - 99)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + bucketSizeNs - 2)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + bucketSizeNs - 1)); - events.push_back(CreateScreenBrightnessChangedEvent( - 456, bucketStartTimeNs + bucketSizeNs + 2)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + 2 * bucketSizeNs - 11)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + 2 * bucketSizeNs - 9)); - events.push_back(CreateScreenBrightnessChangedEvent( - 789, bucketStartTimeNs + 2 * bucketSizeNs - 1)); + events.push_back( + CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 1)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 2 * bucketSizeNs - 10)); + + events.push_back(CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 200)); + events.push_back( + CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1)); + + events.push_back(CreateAcquireWakelockEvent(attributions2, "wl2", + bucketStartTimeNs + bucketSizeNs - 100)); + events.push_back(CreateReleaseWakelockEvent(attributions2, "wl2", + bucketStartTimeNs + 2 * bucketSizeNs - 50)); + + events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 11)); + events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 101)); + events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 201)); + events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + 203)); + events.push_back( + CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 99)); + events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back(CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs + 2)); + events.push_back( + CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 11)); + events.push_back( + CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 9)); + events.push_back( + CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 1)); sortLogEventsByTimestamp(&events); @@ -136,7 +128,10 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -147,8 +142,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { auto data = countMetrics.data(0); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs ); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -158,44 +153,47 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(1); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 111); data = countMetrics.data(2); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 3); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 111); data = countMetrics.data(3); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 333); data = countMetrics.data(4); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -205,24 +203,26 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(5); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 111); data = countMetrics.data(6); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); - ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidDimension(data.dimensions_in_condition(), + android::util::WAKELOCK_STATE_CHANGED, 333); } namespace { @@ -239,8 +239,8 @@ StatsdConfig CreateCountMetricWithLinkConfig() { auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field*/); *config.add_predicate() = screenIsOffPredicate; @@ -252,12 +252,12 @@ StatsdConfig CreateCountMetricWithLinkConfig() { addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); auto metric = config.add_count_metric(); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); metric->set_id(StringToId("AppCrashMetric")); metric->set_what(appCrashMatcher.id()); metric->set_condition(combinationPredicate->id()); - *metric->mutable_dimensions_in_what() = CreateDimensions( - android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */}); + *metric->mutable_dimensions_in_what() = + CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); @@ -267,8 +267,8 @@ StatsdConfig CreateCountMetricWithLinkConfig() { auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); return config; } @@ -279,18 +279,18 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { auto config = CreateCountMetricWithLinkConfig(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector attributions2 = {CreateAttribution(333, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector> events; @@ -311,26 +311,26 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back( + CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 700)); events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300)); events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1)); events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 600)); + events.push_back( + CreateSyncEndEvent(attributions2, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 600)); sortLogEventsByTimestamp(&events); @@ -339,7 +339,10 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -355,20 +358,20 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = countMetrics.data(1); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = countMetrics.data(2); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -378,23 +381,23 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = countMetrics.data(3); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = countMetrics.data(4); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -404,8 +407,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); } namespace { @@ -424,8 +427,8 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field */); *config.add_predicate() = inBatterySaverModePredicate; @@ -438,7 +441,7 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); auto metric = config.add_duration_metric(); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); metric->set_id(StringToId("BatterySaverModeDurationMetric")); metric->set_what(inBatterySaverModePredicate.id()); metric->set_condition(combinationPredicate->id()); @@ -449,26 +452,25 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag } // namespace - TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { - for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { ConfigKey cfgKey; auto config = CreateDurationMetricConfigNoLink(aggregationType); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector attributions1 = { + CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector attributions2 = { + CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector> events; @@ -485,26 +487,26 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870)); events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 800)); events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); + bucketStartTimeNs + bucketSizeNs + 300)); events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1)); events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401)); events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 700)); + bucketStartTimeNs + bucketSizeNs + 700)); sortLogEventsByTimestamp(&events); @@ -513,7 +515,10 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -526,35 +531,35 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = metrics.data(1); EXPECT_FALSE(data.dimensions_in_what().has_field()); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = metrics.data(2); EXPECT_FALSE(data.dimensions_in_what().has_field()); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); } } @@ -572,13 +577,13 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); - *syncDimension = CreateAttributionUidAndTagDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); syncDimension->add_child()->set_field(2 /* name field */); auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */}); *config.add_predicate() = screenIsOffPredicate; *config.add_predicate() = isSyncingPredicate; @@ -590,12 +595,12 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); auto metric = config.add_duration_metric(); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); metric->set_id(StringToId("AppInBackgroundMetric")); metric->set_what(isInBackgroundPredicate.id()); metric->set_condition(combinationPredicate->id()); - *metric->mutable_dimensions_in_what() = CreateDimensions( - android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + *metric->mutable_dimensions_in_what() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); @@ -605,32 +610,32 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); return config; } } // namespace TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { - for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { ConfigKey cfgKey; auto config = CreateDurationMetricConfigWithLink(aggregationType); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = - TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - std::vector attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; + std::vector attributions1 = { + CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; - std::vector attributions2 = - {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; + std::vector attributions2 = { + CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; std::vector> events; @@ -643,26 +648,26 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399)); events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); - events.push_back(CreateScreenStateChangedEvent( - android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 801)); events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); + bucketStartTimeNs + bucketSizeNs + 300)); events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back( + CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1)); events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401)); events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 700)); + bucketStartTimeNs + bucketSizeNs + 700)); sortLogEventsByTimestamp(&events); @@ -671,7 +676,10 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); @@ -685,34 +693,34 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = metrics.data(1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = metrics.data(2); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333); - ValidateAttributionUidAndTagDimension( - data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); } } @@ -722,4 +730,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp index a80fdc5606b755c1a2a0e22015f14883d9c0e322..3843e0a3c67d7bbb6e5a19c1120f904b655c706a 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp @@ -53,7 +53,7 @@ StatsdConfig CreateStatsdConfigForPushedEvent() { fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64) *gaugeMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ }); - gaugeMetric->set_bucket(ONE_MINUTE); + gaugeMetric->set_bucket(FIVE_MINUTES); auto links = gaugeMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); @@ -140,7 +140,10 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; @@ -158,21 +161,21 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().type(), AppStartChanged::HOT); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_name(), "activity_name2"); - EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_msec(), 102L); + EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_millis(), 102L); EXPECT_EQ(data.bucket_info(1).atom_size(), 1); EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().type(), AppStartChanged::WARM); EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_name(), "activity_name4"); - EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_start_msec(), 104L); + EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_start_millis(), 104L); EXPECT_EQ(data.bucket_info(2).atom_size(), 1); EXPECT_EQ(data.bucket_info(2).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.bucket_info(2).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().type(), AppStartChanged::COLD); EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_name(), "activity_name5"); - EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_start_msec(), 105L); + EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_start_millis(), 105L); data = gaugeMetrics.data(1); @@ -186,7 +189,7 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().type(), AppStartChanged::COLD); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_name(), "activity_name7"); - EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_msec(), 201L); + EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_millis(), 201L); } #else @@ -195,4 +198,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index 233031c5c8da4f91d44a23e794d41cdac51f8536..c874d92a1b019b21ae2604b3cc695c550d16b4d7 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -71,7 +71,7 @@ StatsdConfig CreateStatsdConfig() { // The metric is dimensioning by uid only. *countMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); - countMetric->set_bucket(ONE_MINUTE); + countMetric->set_bucket(FIVE_MINUTES); // Links between crash atom and condition of app is in syncing. auto links = countMetric->add_links(); @@ -95,8 +95,10 @@ StatsdConfig CreateStatsdConfig() { } } // namespace - -TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { +// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, +// we should use the real API which will clear the data after dump data is called. +// TODO: better refactor the code so that the tests are not so verbose. +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { auto config = CreateStatsdConfig(); uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = @@ -132,8 +134,8 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2 * bucketSizeNs - 100); - std::vector attributions = - {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; + std::vector attributions = { + CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); auto syncOffEvent1 = @@ -195,7 +197,10 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { processor->OnLogEvent(event.get()); } ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); @@ -208,16 +213,115 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { // Uid field. EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); +} + +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); + auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); + auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); + + auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); + auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); + auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); + + auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); + auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); + + auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); + auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 2 * bucketSizeNs - 100); + + std::vector attributions = { + CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; + auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); + auto syncOffEvent1 = + CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); + auto syncOnEvent2 = + CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); + + auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector buffer; + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); EXPECT_EQ(reports.reports_size(), 1); EXPECT_EQ(reports.reports(0).metrics_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); - data = reports.reports(0).metrics(0).count_metrics().data(0); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); // Validate dimension value. EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -233,4 +337,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index 1186a166dfab8a3e52da24da884bbf2d6a245573..9153795365a2081e02ad94679a600929d59a7fca 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -40,9 +40,11 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); // The predicate is dimensioning by any attribution node and both by uid and tag. - *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateAttributionUidAndTagDimensions( + FieldMatcher dimensions = CreateAttributionUidAndTagDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST}); + // Also slice by the wakelock tag + dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; *config.add_predicate() = holdingWakelockPredicate; auto durationMetric = config.add_duration_metric(); @@ -54,135 +56,269 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(ONE_MINUTE); + durationMetric->set_bucket(FIVE_MINUTES); return config; } +std::vector attributions1 = {CreateAttribution(111, "App1"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + +std::vector attributions2 = {CreateAttribution(111, "App2"), + CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + +/* +Events: +Screen off is met from (200ns,1 min+500ns]. +Acquire event for wl1 from 2ns to 1min+2ns +Acquire event for wl2 from 1min-10ns to 2min-15ns +*/ +void FeedEvents(StatsdConfig config, sp processor) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 500); + + auto acquireEvent1 = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); + auto releaseEvent1 = + CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2); + auto acquireEvent2 = + CreateAcquireWakelockEvent(attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10); + auto releaseEvent2 = CreateReleaseWakelockEvent(attributions2, "wl2", + bucketStartTimeNs + 2 * bucketSizeNs - 15); + + std::vector> events; + + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(acquireEvent1)); + events.push_back(std::move(acquireEvent2)); + events.push_back(std::move(releaseEvent1)); + events.push_back(std::move(releaseEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } +} + } // namespace -TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { ConfigKey cfgKey; - for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) { - auto config = CreateStatsdConfig(aggregationType); - uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + // Only 1 dimension output. The tag dimension in the predicate has been aggregated. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - auto screenTurnedOnEvent = - CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - bucketStartTimeNs + 1); - auto screenTurnedOffEvent = + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // Validate bucket info. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + data = reports.reports(0).metrics(0).duration_metrics().data(0); + // The wakelock holding interval starts from the screen off event and to the end of the 1st + // bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector buffer; + ConfigMetricsReportList reports; + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // Two output buckets. + // The wakelock holding interval in the 1st bucket starts from the screen off event and to + // the end of the 1st bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), + bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); + // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and + // ends at the second screen on event. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); +} +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector buffer; + ConfigMetricsReportList reports; + + std::vector> events; + events.push_back( CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - bucketStartTimeNs + 200); - auto screenTurnedOnEvent2 = - CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, - bucketStartTimeNs + bucketSizeNs + 500); + bucketStartTimeNs + 2 * bucketSizeNs + 90)); + events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3", + bucketStartTimeNs + 2 * bucketSizeNs + 100)); + events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3", + bucketStartTimeNs + 5 * bucketSizeNs + 100)); + sortLogEventsByTimestamp(&events); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The last wakelock holding spans 4 buckets. + EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); + EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + + EXPECT_EQ(reports.reports_size(), 1); - std::vector attributions1 = - {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - - std::vector attributions2 = - {CreateAttribution(111, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - - auto acquireEvent1 = CreateAcquireWakelockEvent( - attributions1, "wl1", bucketStartTimeNs + 2); - auto acquireEvent2 = CreateAcquireWakelockEvent( - attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10); - - auto releaseEvent1 = CreateReleaseWakelockEvent( - attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2); - auto releaseEvent2 = CreateReleaseWakelockEvent( - attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15); - - - std::vector> events; - - events.push_back(std::move(screenTurnedOnEvent)); - events.push_back(std::move(screenTurnedOffEvent)); - events.push_back(std::move(screenTurnedOnEvent2)); - events.push_back(std::move(acquireEvent1)); - events.push_back(std::move(acquireEvent2)); - events.push_back(std::move(releaseEvent1)); - events.push_back(std::move(releaseEvent2)); - - sortLogEventsByTimestamp(&events); - - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - - ConfigMetricsReportList reports; - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); - EXPECT_EQ(reports.reports_size(), 1); - EXPECT_EQ(reports.reports(0).metrics_size(), 1); - // Only 1 dimension output. The tag dimension in the predicate has been aggregated. - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - - auto data = reports.reports(0).metrics(0).duration_metrics().data(0); - // Validate dimension value. - ValidateAttributionUidDimension( - data.dimensions_in_what(), - android::util::WAKELOCK_STATE_CHANGED, 111); - // Validate bucket info. - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); - data = reports.reports(0).metrics(0).duration_metrics().data(0); - // The wakelock holding interval starts from the screen off event and to the end of the 1st - // bucket. - EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); - - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); - EXPECT_EQ(reports.reports_size(), 1); - EXPECT_EQ(reports.reports(0).metrics_size(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - // Dump the report after the end of 2nd bucket. - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); - data = reports.reports(0).metrics(0).duration_metrics().data(0); - // Validate dimension value. - ValidateAttributionUidDimension( - data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); - // Two output buckets. - // The wakelock holding interval in the 1st bucket starts from the screen off event and to - // the end of the 1st bucket. - EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), - bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); - // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and - // ends at the second screen on event. - EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); - - events.clear(); - events.push_back(CreateScreenStateChangedEvent( - android::view::DisplayStateEnum::DISPLAY_STATE_OFF, - bucketStartTimeNs + 2 * bucketSizeNs + 90)); - events.push_back(CreateAcquireWakelockEvent( - attributions1, "wl3", bucketStartTimeNs + 2 * bucketSizeNs + 100)); - events.push_back(CreateReleaseWakelockEvent( - attributions1, "wl3", bucketStartTimeNs + 5 * bucketSizeNs + 100)); - sortLogEventsByTimestamp(&events); - for (const auto& event : events) { - processor->OnLogEvent(event.get()); - } - reports.Clear(); - processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports); - EXPECT_EQ(reports.reports_size(), 1); - EXPECT_EQ(reports.reports(0).metrics_size(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); - data = reports.reports(0).metrics(0).duration_metrics().data(0); - ValidateAttributionUidDimension( - data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); - // The last wakelock holding spans 4 buckets. - EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); - EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); + // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as + // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by + // itself. + EXPECT_EQ(0, reports.reports(0).metrics_size()); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. One dimension with one bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The max is acquire event for wl1 to screen off start. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector buffer; + + std::vector> events; + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 2 * bucketSizeNs + 90)); + events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3", + bucketStartTimeNs + 2 * bucketSizeNs + 100)); + events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3", + bucketStartTimeNs + 5 * bucketSizeNs + 100)); + sortLogEventsByTimestamp(&events); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); } + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The last wakelock holding spans 4 buckets. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 5 * bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 6 * bucketSizeNs); } #else @@ -191,4 +327,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d9c8a8208eef8d19110965e64bba72856d62344 --- /dev/null +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -0,0 +1,269 @@ +// Copyright (C) 2018 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. + +#include "external/puller_util.h" +#include +#include +#include +#include +#include "../metrics/metrics_test_helper.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +using namespace testing; +using std::make_shared; +using std::shared_ptr; +using std::vector; +using testing::Contains; +/* + * Test merge isolated and host uid + */ + +int uidAtomTagId = android::util::CPU_TIME_PER_UID_FREQ; +int nonUidAtomTagId = android::util::SYSTEM_UPTIME; +int timestamp = 1234; +int isolatedUid = 30; +int isolatedAdditiveData = 31; +int isolatedNonAdditiveData = 32; +int hostUid = 20; +int hostAdditiveData = 21; +int hostNonAdditiveData = 22; + +void extractIntoVector(vector> events, + vector>& ret) { + ret.clear(); + status_t err; + for (const auto& event : events) { + vector vec; + vec.push_back(event->GetInt(1, &err)); + vec.push_back(event->GetInt(2, &err)); + vec.push_back(event->GetInt(3, &err)); + ret.push_back(vec); + } +} + +TEST(puller_util, MergeNoDimension) { + vector> inputData; + shared_ptr event = make_shared(uidAtomTagId, timestamp); + // 30->22->31 + event->write(isolatedUid); + event->write(hostNonAdditiveData); + event->write(isolatedAdditiveData); + event->init(); + inputData.push_back(event); + + // 20->22->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(hostUid); + event->write(hostNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + sp uidMap = new NaggyMock(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) + .WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) + .WillRepeatedly(ReturnArg<0>()); + mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + + vector> actual; + extractIntoVector(inputData, actual); + vector expectedV1 = {20, 22, 52}; + EXPECT_EQ(1, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); +} + +TEST(puller_util, MergeWithDimension) { + vector> inputData; + shared_ptr event = make_shared(uidAtomTagId, timestamp); + // 30->32->31 + event->write(isolatedUid); + event->write(isolatedNonAdditiveData); + event->write(isolatedAdditiveData); + event->init(); + inputData.push_back(event); + + // 20->32->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(hostUid); + event->write(isolatedNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + // 20->22->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(hostUid); + event->write(hostNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + sp uidMap = new NaggyMock(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) + .WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) + .WillRepeatedly(ReturnArg<0>()); + mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + + vector> actual; + extractIntoVector(inputData, actual); + vector expectedV1 = {20, 22, 21}; + vector expectedV2 = {20, 32, 52}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, NoMergeHostUidOnly) { + vector> inputData; + shared_ptr event = make_shared(uidAtomTagId, timestamp); + // 20->32->31 + event->write(hostUid); + event->write(isolatedNonAdditiveData); + event->write(isolatedAdditiveData); + event->init(); + inputData.push_back(event); + + // 20->22->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(hostUid); + event->write(hostNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + sp uidMap = new NaggyMock(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) + .WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) + .WillRepeatedly(ReturnArg<0>()); + mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + + // 20->32->31 + // 20->22->21 + vector> actual; + extractIntoVector(inputData, actual); + vector expectedV1 = {20, 32, 31}; + vector expectedV2 = {20, 22, 21}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, IsolatedUidOnly) { + vector> inputData; + shared_ptr event = make_shared(uidAtomTagId, timestamp); + // 30->32->31 + event->write(hostUid); + event->write(isolatedNonAdditiveData); + event->write(isolatedAdditiveData); + event->init(); + inputData.push_back(event); + + // 30->22->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(hostUid); + event->write(hostNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + sp uidMap = new NaggyMock(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) + .WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) + .WillRepeatedly(ReturnArg<0>()); + mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + + // 20->32->31 + // 20->22->21 + vector> actual; + extractIntoVector(inputData, actual); + vector expectedV1 = {20, 32, 31}; + vector expectedV2 = {20, 22, 21}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, MultipleIsolatedUidToOneHostUid) { + vector> inputData; + shared_ptr event = make_shared(uidAtomTagId, timestamp); + // 30->32->31 + event->write(isolatedUid); + event->write(isolatedNonAdditiveData); + event->write(isolatedAdditiveData); + event->init(); + inputData.push_back(event); + + // 31->32->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(isolatedUid + 1); + event->write(isolatedNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + // 20->32->21 + event = make_shared(uidAtomTagId, timestamp); + event->write(hostUid); + event->write(isolatedNonAdditiveData); + event->write(hostAdditiveData); + event->init(); + inputData.push_back(event); + + sp uidMap = new NaggyMock(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); + mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId); + + vector> actual; + extractIntoVector(inputData, actual); + vector expectedV1 = {20, 32, 73}; + EXPECT_EQ(1, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); +} + +TEST(puller_util, NoNeedToMerge) { + vector> inputData; + shared_ptr event = + make_shared(nonUidAtomTagId, timestamp); + // 32 + event->write(isolatedNonAdditiveData); + event->init(); + inputData.push_back(event); + + event = make_shared(nonUidAtomTagId, timestamp); + // 22 + event->write(hostNonAdditiveData); + event->init(); + inputData.push_back(event); + + sp uidMap = new NaggyMock(); + mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId); + + EXPECT_EQ(2, (int)inputData.size()); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index a1343002405b40353fa6b099e13c0b0b2581ede6..bd114439b83c31a2a5729c97573160072a4b8b5a 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -225,7 +225,7 @@ TEST(StatsdStatsTest, TestAtomLog) { bool dropboxAtomGood = false; for (const auto& atomStats : report.atom_stats()) { - if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 2) { + if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) { sensorAtomGood = true; } if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) { diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 50b3532e827a62f9acea2e216877435190669a73..20ddbe9f0e38e8d5ca13fedb6f843b4d43ede3cc 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -13,7 +13,6 @@ // limitations under the License. #include "src/metrics/CountMetricProducer.h" -#include "src/dimension.h" #include "src/stats_log_util.h" #include "metrics_test_helper.h" #include "tests/statsd_test_util.h" @@ -49,12 +48,15 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { metric.set_bucket(ONE_MINUTE); LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); sp wizard = new NaggyMock(); CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); // 2 events in bucket 1. countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); @@ -77,6 +79,8 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { // 1 matched event happens in bucket 2. LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2); + event3.init(); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); @@ -107,11 +111,15 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { metric.set_condition(StringToId("SCREEN_ON")); LogEvent event1(1, bucketStartTimeNs + 1); + event1.init(); + LogEvent event2(1, bucketStartTimeNs + 10); + event2.init(); sp wizard = new NaggyMock(); CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); countProducer.onConditionChanged(true, bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); @@ -173,6 +181,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); @@ -191,6 +200,120 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_EQ(1LL, bucketInfo.mCount); } +TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + Alert alert; + alert.set_num_buckets(3); + alert.set_trigger_if_sum_gt(2); + LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.write("111"); // uid + event1.init(); + sp wizard = new NaggyMock(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); + + sp anomalyTracker = countProducer.addAnomalyTracker(alert); + EXPECT_TRUE(anomalyTracker != nullptr); + + // Bucket is flushed yet. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // App upgrade forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted. + countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((long long)bucketStartTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ((long long)eventUpgradeTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + // Anomaly tracker only contains full buckets. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + uint64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); + // Next event occurs in same bucket as partial bucket created. + LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10); + event2.write("222"); // uid + event2.init(); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Third event in following bucket. + LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10); + event3.write("333"); // uid + event3.init(); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.write("111"); // uid + event1.init(); + sp wizard = new NaggyMock(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); + + // Bucket is flushed yet. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + // App upgrade forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted. + countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)bucketStartTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + (uint64_t)countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + + // Next event occurs in same bucket as partial bucket created. + LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10); + event2.write("222"); // uid + event2.init(); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + + // Third event in following bucket. + LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10); + event3.write("333"); // uid + event3.init(); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + (uint64_t)countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); +} + TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { Alert alert; alert.set_id(11); @@ -212,16 +335,25 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { sp wizard = new NaggyMock(); CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); + sp anomalyTracker = countProducer.addAnomalyTracker(alert); int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1); + event3.init(); LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1); + event4.init(); LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2); + event5.init(); LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3); + event6.init(); LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC); + event7.init(); // Two events in bucket #0. countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); @@ -245,13 +377,13 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index c9fe2523c577d520b10e5d95a8cd876b571d2557..79695967a6ddc9eeb9a35b7a7647067abe10bf1c 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -51,12 +51,15 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); + event2.init(); FieldMatcher dimensions; DurationMetricProducer durationProducer( kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); @@ -86,14 +89,20 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); + event3.init(); LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); + event4.init(); FieldMatcher dimensions; DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -116,6 +125,257 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { EXPECT_EQ(1ULL, buckets2[0].mDuration); } +TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { + /** + * The duration starts from the first bucket, through the two partial buckets (10-70sec), + * another bucket, and ends at the beginning of the next full bucket. + * Expected buckets: + * - [10,25]: 14 secs + * - [25,70]: All 45 secs + * - [70,130]: All 60 secs + * - [130, 210]: Only 5 secs (event ended at 135sec) + */ + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + uint64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + uint64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + sp wizard = new NaggyMock(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + std::vector buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); + buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(3UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); + EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { + /** + * Expected buckets (start at 11s, upgrade at 75s, end at 135s): + * - [10,70]: 59 secs + * - [70,75]: 5 sec + * - [75,130]: 55 secs + */ + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + uint64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + uint64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + sp wizard = new NaggyMock(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + std::vector buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); + buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(3UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + uint64_t startTimeNs = bucketStartTimeNs + 1; + uint64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; + + int tagId = 1; + + // Setup metric with alert. + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + Alert alert; + alert.set_num_buckets(3); + alert.set_trigger_if_sum_gt(2); + + sp wizard = new NaggyMock(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + + sp anomalyTracker = durationProducer.addAnomalyTracker(alert); + EXPECT_TRUE(anomalyTracker != nullptr); + + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); + + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, + (uint64_t)anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + uint64_t startTimeNs = bucketStartTimeNs + 1; + uint64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); + LogEvent event1(tagId, startTimeNs); + event1.write("111"); // uid + event1.init(); + sp wizard = new NaggyMock(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); + std::vector buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + uint64_t startTimeNs = bucketStartTimeNs + 1; + uint64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; + + int tagId = 1; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); + LogEvent event1(tagId, startTimeNs); + event1.write("111"); // uid + event1.init(); + sp wizard = new NaggyMock(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer( + kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // Stop occurs in the same partial bucket as created for the app upgrade. + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + std::vector buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 3deab3710dd105a42c685abc4504e38528cb3a8b..8246268d88e78e84c0e497f10a116ce121e64955 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -13,7 +13,6 @@ // limitations under the License. #include "src/metrics/EventMetricProducer.h" -#include "src/dimension.h" #include "metrics_test_helper.h" #include "tests/statsd_test_util.h" diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 58be5b071b53190fe1e6bc2bb78f1b2747155e58..0eb8ce2603bdc32ee022f897c850902a2e0e3f6f 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -44,6 +44,7 @@ const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000L const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; +const uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; TEST(GaugeMetricProducerTest, TestNoCondition) { GaugeMetric metric; @@ -66,6 +67,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); vector> allData; allData.clear(); @@ -79,9 +81,10 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(10, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10, it->mValue.int_value); it++; - EXPECT_EQ(11, it->second.value_int()); + EXPECT_EQ(11, it->mValue.int_value); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); allData.clear(); @@ -95,16 +98,20 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); - EXPECT_EQ(24, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24, it->mValue.int_value); it++; - EXPECT_EQ(25, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25, it->mValue.int_value); // One dimension. EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size()); it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(10L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10L, it->mValue.int_value); it++; - EXPECT_EQ(11L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(11L, it->mValue.int_value); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); @@ -113,12 +120,153 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); - EXPECT_EQ(24L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24L, it->mValue.int_value); it++; - EXPECT_EQ(25L, it->second.value_int()); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25L, it->mValue.int_value); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); } +TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(true); + + Alert alert; + alert.set_id(101); + alert.set_metric_id(metricId); + alert.set_trigger_if_sum_gt(25); + alert.set_num_buckets(100); + sp wizard = new NaggyMock(); + shared_ptr pullerManager = + make_shared>(); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + -1 /* -1 means no pulling */, bucketStartTimeNs, + pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); + sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert); + EXPECT_TRUE(anomalyTracker != nullptr); + + shared_ptr event1 = make_shared(tagId, bucketStartTimeNs + 10); + event1->write(1); + event1->write(10); + event1->init(); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); + EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0UL, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + // Partial buckets are not sent to anomaly tracker. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Create an event in the same partial bucket. + shared_ptr event2 = make_shared(tagId, bucketStartTimeNs + 59 * NS_PER_SEC); + event2->write(1); + event2->write(10); + event2->init(); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); + EXPECT_EQ(0UL, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + // Partial buckets are not sent to anomaly tracker. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Next event should trigger creation of new bucket and send previous full bucket to anomaly + // tracker. + shared_ptr event3 = make_shared(tagId, bucketStartTimeNs + 65 * NS_PER_SEC); + event3->write(1); + event3->write(10); + event3->init(); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); + EXPECT_EQ(1UL, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Next event should trigger creation of new bucket. + shared_ptr event4 = + make_shared(tagId, bucketStartTimeNs + 125 * NS_PER_SEC); + event4->write(1); + event4->write(10); + event4->init(); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); + EXPECT_EQ(2UL, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + sp wizard = new NaggyMock(); + + shared_ptr pullerManager = + make_shared>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, eventUpgradeTimeNs); + event->write("some value"); + event->write(2); + event->init(); + data->push_back(event); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); + + vector> allData; + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 1); + event->write("some value"); + event->write(1); + event->init(); + allData.push_back(event); + gaugeProducer.onDataPulled(allData); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0UL, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ((uint64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + allData.clear(); + event = make_shared(tagId, bucketStartTimeNs + bucketSizeNs + 1); + event->write("some value"); + event->write(3); + event->init(); + allData.push_back(event); + gaugeProducer.onDataPulled(allData); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); +} + TEST(GaugeMetricProducerTest, TestWithCondition) { GaugeMetric metric; metric.set_id(metricId); @@ -147,12 +295,14 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(100, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); vector> allData; @@ -165,19 +315,26 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(110, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back() - .mGaugeAtoms.front().mFields->begin()->second.value_int()); + EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back() - .mGaugeAtoms.front().mFields->begin()->second.value_int()); + EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); } @@ -197,6 +354,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeFieldMatcher->add_child()->set_field(2); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); Alert alert; alert.set_id(101); @@ -215,9 +373,10 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeProducer.onDataPulled({event1}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(13L, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); std::shared_ptr event2 = @@ -228,11 +387,12 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeProducer.onDataPulled({event2}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(15L, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); std::shared_ptr event3 = std::make_shared(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10); @@ -242,11 +402,12 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeProducer.onDataPulled({event3}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(26L, - gaugeProducer.mCurrentSlicedBucket->begin()-> - second.front().mFields->begin()->second.value_int()); + EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); // The event4 does not have the gauge field. Thus the current bucket value is 0. std::shared_ptr event4 = diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 203f028080d2b8033fd93dd0a7ad8e49aef9742e..a164c12134b592878c23df6ede896b8dd2663118 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -41,6 +41,11 @@ const ConfigKey kConfigKey(0, 12345); const int TagId = 1; +const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1"); +const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; +const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); +const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); +const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); @@ -48,17 +53,20 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; + sp wizard = new NaggyMock(); unordered_map> buckets; uint64_t bucketStartTimeNs = 10000000000; - uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, false, {}); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + false, {}); tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey()); // Event starts again. This would not change anything as it already starts. @@ -82,17 +90,20 @@ TEST(MaxDurationTrackerTest, TestStopAll) { const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; - uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, false, {}); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + false, {}); tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey()); @@ -101,15 +112,14 @@ TEST(MaxDurationTrackerTest, TestStopAll) { tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets); tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); EXPECT_TRUE(tracker.mInfos.empty()); + EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); + tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets); EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); EXPECT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - - tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets); - EXPECT_EQ(2u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(40ULL, buckets[eventKey][1].mDuration); + EXPECT_EQ(bucketSizeNs + 40 - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[eventKey][0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs); } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { @@ -117,17 +127,20 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; - uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, false, {}); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + false, {}); // The event starts. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); @@ -137,14 +150,18 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { ConditionKey()); // The event stops at early 4th bucket. + // Notestop is called from DurationMetricProducer's onMatchedLogEvent, which calls + // flushIfneeded. tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets); tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - EXPECT_EQ(3u, buckets[eventKey].size()); - EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][1].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][2].mDuration); + EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); + + tracker.flushIfNeeded(bucketStartTimeNs + 4 * bucketSizeNs, &buckets); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ((3 * bucketSizeNs) + 20 - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[eventKey][0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs); } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { @@ -152,17 +169,20 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; - uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, false, {}); + true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + false, {}); // 2 starts tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); @@ -171,98 +191,217 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/); tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets); - - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - EXPECT_EQ(2u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); + // Because of nesting, still not stopped. + EXPECT_TRUE(buckets.find(eventKey) == buckets.end()); // real stop now. tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets); - EXPECT_EQ(3u, buckets[eventKey].size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); - EXPECT_EQ(5ULL, buckets[eventKey][2].mDuration); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(2 * bucketSizeNs + 5 - 1, buckets[eventKey][0].mDuration); } TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { + const std::vector conditionKey = {key1}; + + vector dimensionInCondition; + sp wizard = new NaggyMock(); + + ConditionKey conditionKey1; + MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 1, "1"); + conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; + + /** + Start in first bucket, stop in second bucket. Condition turns on and off in the first bucket + and again turns on and off in the second bucket. + */ + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t eventStartTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + uint64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC; + uint64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC; + uint64_t conditionStarts2 = bucketStartTimeNs + bucketSizeNs + 5 * NS_PER_SEC; + uint64_t conditionStops2 = conditionStarts2 + 10 * NS_PER_SEC; + uint64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC; + + int64_t metricId = 1; + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true, + {}); + EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); + + tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); + tracker.noteConditionChanged(key1, true, conditionStarts1); + tracker.noteConditionChanged(key1, false, conditionStops1); + unordered_map> buckets; + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_EQ(0U, buckets.size()); + + tracker.noteConditionChanged(key1, true, conditionStarts2); + tracker.noteConditionChanged(key1, false, conditionStops2); + tracker.noteStop(key1, eventStopTimeNs, false); + tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1, &buckets); + EXPECT_EQ(1U, buckets.size()); + vector item = buckets.begin()->second; + EXPECT_EQ(1UL, item.size()); + EXPECT_EQ(13ULL * NS_PER_SEC, item[0].mDuration); +} + +TEST(MaxDurationTrackerTest, TestAnomalyDetection) { const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); ConditionKey conditionKey1; MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - EXPECT_CALL(*wizard, query(_, conditionKey1, _, _)) // #4 - .WillOnce(Return(ConditionState::kFalse)); - unordered_map> buckets; - uint64_t bucketStartTimeNs = 10000000000; - uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; + uint64_t eventStartTimeNs = 13000000000; int64_t durationTimeNs = 2 * 1000; int64_t metricId = 1; + Alert alert; + alert.set_id(101); + alert.set_metric_id(1); + alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); + alert.set_num_buckets(2); + const int32_t refPeriodSec = 45; + alert.set_refractory_period_secs(refPeriodSec); + sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, true, {}); - EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + true, {anomalyTracker}); tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); + sp alarm = anomalyTracker->mAlarms.begin()->second; + EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); + + // Remove the anomaly alarm when the duration is no longer fully met. + tracker.noteConditionChanged(key1, false, eventStartTimeNs + 15 * NS_PER_SEC); + EXPECT_EQ(0U, anomalyTracker->mAlarms.size()); + + // Since the condition was off for 10 seconds, the anomaly should trigger 10 sec later. + tracker.noteConditionChanged(key1, true, eventStartTimeNs + 25 * NS_PER_SEC); + EXPECT_EQ(1U, anomalyTracker->mAlarms.size()); + alarm = anomalyTracker->mAlarms.begin()->second; + EXPECT_EQ((long long)(63ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); +} - tracker.onSlicedConditionMayChange(eventStartTimeNs + 5); +// This tests that we correctly compute the predicted time of an anomaly assuming that the current +// state continues forward as-is. +TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { + const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; - tracker.noteStop(key1, eventStartTimeNs + durationTimeNs, false); + vector dimensionInCondition; + sp wizard = new NaggyMock(); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); - EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); - EXPECT_EQ(1u, buckets[eventKey].size()); - EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration); -} + ConditionKey conditionKey1; + MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); + conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; + ConditionKey conditionKey2; + conditionKey2[StringToId("APP_BACKGROUND")] = {getMockedDimensionKey(TagId, 4, "2")}; + + unordered_map> buckets; + + /** + * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these + * nested dimensions, we enter the pause state after 3 seconds. When we resume, the second + * dimension has already been running for 4 seconds. Thus, we have 40-4=36 seconds remaining + * before we trigger the anomaly. + */ + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; + uint64_t eventStartTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start. + uint64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC; + uint64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC; + uint64_t conditionStarts2 = bucketStartTimeNs + 20 * NS_PER_SEC; + uint64_t eventStartTimeNs2 = conditionStarts2 - 4 * NS_PER_SEC; -TEST(MaxDurationTrackerTest, TestAnomalyDetection) { - const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); - const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); - const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - FieldMatcher dimensionInCondition; int64_t metricId = 1; Alert alert; alert.set_id(101); - alert.set_metric_id(metricId); - alert.set_trigger_if_sum_gt(32 * NS_PER_SEC); + alert.set_metric_id(1); + alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); alert.set_num_buckets(2); - const int32_t refPeriodSec = 1; + const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); + sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + true, {anomalyTracker}); + + tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); + tracker.noteConditionChanged(key1, true, conditionStarts1); + tracker.noteConditionChanged(key1, false, conditionStops1); + tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); // Condition is on already. + tracker.noteConditionChanged(key1, true, conditionStarts2); + EXPECT_EQ(1U, anomalyTracker->mAlarms.size()); + auto alarm = anomalyTracker->mAlarms.begin()->second; + EXPECT_EQ(conditionStarts2 + 36 * NS_PER_SEC, + (unsigned long long)(alarm->timestampSec * NS_PER_SEC)); +} - unordered_map> buckets; - sp wizard = new NaggyMock(); +// Suppose that within one tracker there are two dimensions A and B. +// Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the +// elapsed duration of B. +TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { + const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; - uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; - uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - uint64_t bucketSizeNs = 30 * NS_PER_SEC; + vector dimensionInCondition; + sp wizard = new NaggyMock(); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); + ConditionKey conditionKey1; + MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); + conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; + ConditionKey conditionKey2; + conditionKey2[StringToId("APP_BACKGROUND")] = {getMockedDimensionKey(TagId, 4, "2")}; - tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey()); - tracker.noteStop(key1, eventStartTimeNs + 10, false); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); - EXPECT_EQ(10LL, tracker.mDuration); + unordered_map> buckets; - tracker.noteStart(key2, true, eventStartTimeNs + 20, ConditionKey()); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets); - tracker.noteStop(key2, eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false); - EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration); + /** + * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these + * nested dimensions, are started for 8 seconds. When we stop, the other nested dimension has + * been started for 5 seconds. So we can only allow 35 more seconds from now. + */ + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs; + uint64_t bucketNum = 0; + uint64_t eventStartTimeNs1 = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start. + uint64_t eventStopTimeNs1 = bucketStartTimeNs + 13 * NS_PER_SEC; + uint64_t eventStartTimeNs2 = bucketStartTimeNs + 8 * NS_PER_SEC; - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), - (eventStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + refPeriodSec); + int64_t metricId = 1; + Alert alert; + alert.set_id(101); + alert.set_metric_id(1); + alert.set_trigger_if_sum_gt(40 * NS_PER_SEC); + alert.set_num_buckets(2); + const int32_t refPeriodSec = 45; + alert.set_refractory_period_secs(refPeriodSec); + sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, + true, {anomalyTracker}); + + tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1); + tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); + tracker.noteStop(key1, eventStopTimeNs1, false); + EXPECT_EQ(1U, anomalyTracker->mAlarms.size()); + auto alarm = anomalyTracker->mAlarms.begin()->second; + EXPECT_EQ(eventStopTimeNs1 + 35 * NS_PER_SEC, + (unsigned long long)(alarm->timestampSec * NS_PER_SEC)); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 80e16a1f3b55557e53f0daa1146c7c69aa59deb7..cb731c555e908c56e5fb051660e56192366e085a 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -38,6 +38,12 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); const int TagId = 1; const int64_t metricId = 123; +const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event"); + +const std::vector kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; +const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); +const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); +const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; TEST(OringDurationTrackerTest, TestDurationOverlap) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); @@ -46,18 +52,20 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; - uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, false, {}); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -79,17 +87,19 @@ TEST(OringDurationTrackerTest, TestDurationNested) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; - uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, false, {}); + true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl @@ -110,17 +120,19 @@ TEST(OringDurationTrackerTest, TestStopAll) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; - uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, false, {}); + true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl @@ -140,18 +152,20 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); unordered_map> buckets; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; - uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, false, {}); + true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -179,7 +193,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); ConditionKey key1; @@ -190,13 +204,15 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { unordered_map> buckets; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; - uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, true, {}); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -217,7 +233,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); ConditionKey key1; @@ -231,12 +247,14 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { unordered_map> buckets; uint64_t bucketStartTimeNs = 10000000000; - uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketNum = 0; + uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - false, bucketStartTimeNs, bucketSizeNs, true, {}); + false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); // condition to false; record duration 5n @@ -259,7 +277,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; sp wizard = new NaggyMock(); ConditionKey key1; @@ -271,11 +289,13 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { unordered_map> buckets; uint64_t bucketStartTimeNs = 10000000000; - uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t bucketNum = 0; + uint64_t eventStartTimeNs = bucketStartTimeNs + 1; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, true, {}); + true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1); @@ -299,7 +319,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -311,12 +331,13 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { sp wizard = new NaggyMock(); uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker}); + true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, true, {anomalyTracker}); // Nothing in the past bucket. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); @@ -366,7 +387,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { const std::vector kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -379,26 +400,27 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { sp wizard = new NaggyMock(); uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; + uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); + true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs, + bucketSizeNs, false, {anomalyTracker}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); EXPECT_TRUE(tracker.mStarted.empty()); - EXPECT_EQ(10LL, tracker.mDuration); + EXPECT_EQ(10LL, tracker.mDuration); // 10ns EXPECT_EQ(0u, tracker.mStarted.size()); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey()); EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); - EXPECT_EQ((long long)(51ULL * NS_PER_SEC), + EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); - // The alarm is set to fire at 51s, and when it does, an anomaly would be declared. However, + // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However, // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s. tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets); @@ -415,7 +437,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { {getMockedDimensionKey(TagId, 1, "maps")}; const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); - FieldMatcher dimensionInCondition; + vector dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -429,13 +451,12 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { ConditionKey conkey; conkey[StringToId("APP_BACKGROUND")] = kConditionKey1; uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; - uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, - true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, - {anomalyTracker}); + true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs, + bucketSizeNs, false, {anomalyTracker}); tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1 EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 55c078dcbab808d99507f97d3714a3cd2fd3cb57..ce4fa327849318240e055f7fb4906066544041ec 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -44,6 +44,7 @@ const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000L const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; +const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; /* * Tests pulled atoms with no conditions @@ -65,6 +66,7 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); vector> allData; allData.clear(); @@ -78,6 +80,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + valueProducer.setBucketSize(60 * NS_PER_SEC); + // startUpdated:true tainted:0 sum:0 start:11 EXPECT_EQ(true, curInterval.startUpdated); EXPECT_EQ(0, curInterval.tainted); @@ -161,7 +165,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, pullerManager); - + valueProducer.setBucketSize(60 * NS_PER_SEC); valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); // has one slice @@ -202,6 +206,103 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { EXPECT_EQ(false, curInterval.startUpdated); } +TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + + sp wizard = new NaggyMock(); + shared_ptr pullerManager = + make_shared>(); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, + pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); + + shared_ptr event1 = make_shared(tagId, bucketStartTimeNs + 10); + event1->write(1); + event1->write(10); + event1->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + + valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs); + + shared_ptr event2 = make_shared(tagId, bucketStartTimeNs + 59 * NS_PER_SEC); + event2->write(1); + event2->write(10); + event2->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); + EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs); + + // Next value should create a new bucket. + shared_ptr event3 = make_shared(tagId, bucketStartTimeNs + 65 * NS_PER_SEC); + event3->write(1); + event3->write(10); + event3->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); + EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); +} + +TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + + sp wizard = new NaggyMock(); + shared_ptr pullerManager = + make_shared>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 10); + event->write(tagId); + event->write(120); + event->init(); + data->push_back(event); + return true; + })); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs, + pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); + + vector> allData; + allData.clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 1); + event->write(tagId); + event->write(100); + event->init(); + allData.push_back(event); + + valueProducer.onDataPulled(allData); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + + valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue); + + allData.clear(); + event = make_shared(tagId, bucket2StartTimeNs + 1); + event->write(tagId); + event->write(150); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData); + EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((uint64_t)bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue); +} + TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { ValueMetric metric; metric.set_id(metricId); @@ -215,6 +316,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); shared_ptr event1 = make_shared(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -261,6 +363,8 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { sp wizard = new NaggyMock(); ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -1 /*not pulled*/, bucketStartTimeNs); + valueProducer.setBucketSize(60 * NS_PER_SEC); + sp anomalyTracker = valueProducer.addAnomalyTracker(alert); @@ -310,16 +414,16 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); // Anomaly at event 4 since Value sum == 131 > 130! EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5); // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6); // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp index ab9345af172e9737f3bf3c1e75b07b444daa070b..7b9c0d6ab28e8266908b80191ecff0ae2c818bdd 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp @@ -19,20 +19,26 @@ namespace os { namespace statsd { HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(tagId); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value); - return HashableDimensionKey(dimensionsValue); + HashableDimensionKey dimension; + int pos[] = {key, 0, 0}; + dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value))); + + return dimension; } MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) { - DimensionsValue dimensionsValue; - dimensionsValue.set_field(tagId); - dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key); - dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value); - return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY); + return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY); +} + +void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) { + matcher->set_field(tagId); } + +void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) { + matcher->set_field(tagId); + matcher->add_child()->set_field(fieldNum); +} + } // namespace statsd } // namespace os } // namespace android \ No newline at end of file diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index 0a97456c56847f99ff422a51ee54a6bbc47d1acd..a01de6334ab4fab7a799361695bf26b2b86a2ee2 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -15,6 +15,7 @@ #include "src/condition/ConditionWizard.h" #include "src/external/StatsPullerManager.h" +#include "src/packages/UidMap.h" #include #include @@ -25,12 +26,10 @@ namespace statsd { class MockConditionWizard : public ConditionWizard { public: - MOCK_METHOD4( - query, - ConditionState(const int conditionIndex, - const ConditionKey& conditionParameters, - const FieldMatcher& dimensionFields, - std::unordered_set *dimensionKeySet)); + MOCK_METHOD4(query, + ConditionState(const int conditionIndex, const ConditionKey& conditionParameters, + const vector& dimensionFields, + std::unordered_set* dimensionKeySet)); }; class MockStatsPullerManager : public StatsPullerManager { @@ -40,9 +39,18 @@ public: MOCK_METHOD2(Pull, bool(const int pullCode, vector>* data)); }; +class MockUidMap : public UidMap { + public: + MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid)); +}; + HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value); +// Utils to build FieldMatcher proto for simple one-depth atoms. +void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher); +void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher); + } // namespace statsd } // namespace os } // namespace android \ No newline at end of file diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 13055cb9e7a30226e34a38dc13b973414593319a..7568348108c40e7f5813d678cf9b7731b20c5811 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -186,7 +186,7 @@ Predicate CreateScreenIsOnPredicate() { Predicate CreateScreenIsOffPredicate() { Predicate predicate; - predicate.set_id(StringToId("ScreenIsOff")); + predicate.set_id(1111123); predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); return predicate; @@ -202,7 +202,7 @@ Predicate CreateHoldingWakelockPredicate() { Predicate CreateIsSyncingPredicate() { Predicate predicate; - predicate.set_id(StringToId("IsSyncing")); + predicate.set_id(33333333333333); predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); return predicate; @@ -291,8 +291,8 @@ std::unique_ptr CreateScreenBrightnessChangedEvent( } std::unique_ptr CreateWakelockStateChangedEvent( - const std::vector& attributions, const string& wakelockName, - const WakelockStateChanged::State state, uint64_t timestampNs) { + const std::vector& attributions, const string& wakelockName, + const WakelockStateChanged::State state, uint64_t timestampNs) { auto event = std::make_unique(android::util::WAKELOCK_STATE_CHANGED, timestampNs); event->write(attributions); event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); @@ -303,15 +303,15 @@ std::unique_ptr CreateWakelockStateChangedEvent( } std::unique_ptr CreateAcquireWakelockEvent( - const std::vector& attributions, - const string& wakelockName, uint64_t timestampNs) { + const std::vector& attributions, const string& wakelockName, + uint64_t timestampNs) { return CreateWakelockStateChangedEvent( attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs); } std::unique_ptr CreateReleaseWakelockEvent( - const std::vector& attributions, - const string& wakelockName, uint64_t timestampNs) { + const std::vector& attributions, const string& wakelockName, + uint64_t timestampNs) { return CreateWakelockStateChangedEvent( attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs); } @@ -339,8 +339,8 @@ std::unique_ptr CreateMoveToForegroundEvent(const int uid, uint64_t ti } std::unique_ptr CreateSyncStateChangedEvent( - const std::vector& attributions, - const string& name, const SyncStateChanged::State state, uint64_t timestampNs) { + const std::vector& attributions, const string& name, + const SyncStateChanged::State state, uint64_t timestampNs) { auto event = std::make_unique(android::util::SYNC_STATE_CHANGED, timestampNs); event->write(attributions); event->write(name); @@ -350,12 +350,14 @@ std::unique_ptr CreateSyncStateChangedEvent( } std::unique_ptr CreateSyncStartEvent( - const std::vector& attributions, const string& name, uint64_t timestampNs){ + const std::vector& attributions, const string& name, + uint64_t timestampNs) { return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs); } std::unique_ptr CreateSyncEndEvent( - const std::vector& attributions, const string& name, uint64_t timestampNs) { + const std::vector& attributions, const string& name, + uint64_t timestampNs) { return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs); } @@ -396,8 +398,8 @@ sp CreateStatsLogProcessor(const long timeBaseSec, const Stat return processor; } -AttributionNode CreateAttribution(const int& uid, const string& tag) { - AttributionNode attribution; +AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) { + AttributionNodeInternal attribution; attribution.set_uid(uid); attribution.set_tag(tag); return attribution; @@ -406,7 +408,7 @@ AttributionNode CreateAttribution(const int& uid, const string& tag) { void sortLogEventsByTimestamp(std::vector> *events) { std::sort(events->begin(), events->end(), [](const std::unique_ptr& a, const std::unique_ptr& b) { - return a->GetTimestampNs() < b->GetTimestampNs(); + return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); }); } @@ -461,6 +463,93 @@ void ValidateAttributionUidAndTagDimension( .value_tuple().dimensions_value(1).value_str(), tag); } +bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { + if (s1.field() != s2.field()) { + return false; + } + if (s1.value_case() != s2.value_case()) { + return false; + } + switch (s1.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return (s1.value_str() == s2.value_str()); + case DimensionsValue::ValueCase::kValueInt: + return s1.value_int() == s2.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return s1.value_long() == s2.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return s1.value_bool() == s2.value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return s1.value_float() == s2.value_float(); + case DimensionsValue::ValueCase::kValueTuple: { + if (s1.value_tuple().dimensions_value_size() != + s2.value_tuple().dimensions_value_size()) { + return false; + } + bool allMatched = true; + for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { + allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i)); + } + return allMatched; + } + case DimensionsValue::ValueCase::VALUE_NOT_SET: + default: + return true; + } +} + +bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) { + if (s1.field() != s2.field()) { + return s1.field() < s2.field(); + } + if (s1.value_case() != s2.value_case()) { + return s1.value_case() < s2.value_case(); + } + switch (s1.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return s1.value_str() < s2.value_str(); + case DimensionsValue::ValueCase::kValueInt: + return s1.value_int() < s2.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return s1.value_long() < s2.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return (int)s1.value_bool() < (int)s2.value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return s1.value_float() < s2.value_float(); + case DimensionsValue::ValueCase::kValueTuple: { + if (s1.value_tuple().dimensions_value_size() != + s2.value_tuple().dimensions_value_size()) { + return s1.value_tuple().dimensions_value_size() < + s2.value_tuple().dimensions_value_size(); + } + for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) { + if (EqualsTo(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i))) { + continue; + } else { + return LessThan(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i)); + } + } + return false; + } + case DimensionsValue::ValueCase::VALUE_NOT_SET: + default: + return false; + } +} + +bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) { + if (LessThan(s1.dimInWhat, s2.dimInWhat)) { + return true; + } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) { + return false; + } + + return LessThan(s1.dimInCondition, s2.dimInCondition); +} + } // namespace statsd } // namespace os } // namespace android \ No newline at end of file diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 6638893f6aebcc1babdc8a470c32b8b1dd8804a1..1708cc31bf14f8ce7447787ee2eeaf31897895c7 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -15,10 +15,11 @@ #pragma once #include +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "statslog.h" -#include "src/logd/LogEvent.h" #include "src/StatsLogProcessor.h" +#include "src/logd/LogEvent.h" +#include "statslog.h" namespace android { namespace os { @@ -119,11 +120,13 @@ std::unique_ptr CreateMoveToForegroundEvent(const int uid, uint64_t ti // Create log event when the app sync starts. std::unique_ptr CreateSyncStartEvent( - const std::vector& attributions, const string& name, uint64_t timestampNs); + const std::vector& attributions, const string& name, + uint64_t timestampNs); // Create log event when the app sync ends. std::unique_ptr CreateSyncEndEvent( - const std::vector& attributions, const string& name, uint64_t timestampNs); + const std::vector& attributions, const string& name, + uint64_t timestampNs); // Create log event when the app sync ends. std::unique_ptr CreateAppCrashEvent( @@ -131,20 +134,20 @@ std::unique_ptr CreateAppCrashEvent( // Create log event for acquiring wakelock. std::unique_ptr CreateAcquireWakelockEvent( - const std::vector& attributions, - const string& wakelockName, uint64_t timestampNs); + const std::vector& attributions, const string& wakelockName, + uint64_t timestampNs); // Create log event for releasing wakelock. std::unique_ptr CreateReleaseWakelockEvent( - const std::vector& attributions, - const string& wakelockName, uint64_t timestampNs); + const std::vector& attributions, const string& wakelockName, + uint64_t timestampNs); // Create log event for releasing wakelock. std::unique_ptr CreateIsolatedUidChangedEvent( int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs); -// Helper function to create an AttributionNode proto. -AttributionNode CreateAttribution(const int& uid, const string& tag); +// Helper function to create an AttributionNodeInternal proto. +AttributionNodeInternal CreateAttribution(const int& uid, const string& tag); // Create a statsd log event processor upon the start time in seconds, config and key. sp CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, @@ -159,14 +162,30 @@ void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, i void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int atomId, int uid, const std::string& tag); +struct DimensionsPair { + DimensionsPair(DimensionsValue m1, DimensionsValue m2) : dimInWhat(m1), dimInCondition(m2){}; + + DimensionsValue dimInWhat; + DimensionsValue dimInCondition; +}; + +bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2); +bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2); + +struct DimensionCompare { + bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const { + return LessThan(s1, s2); + } +}; + template void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) { - std::map dimensionIndexMap; + std::map dimensionIndexMap; for (int i = 0; i < metricData.data_size(); ++i) { - dimensionIndexMap.insert(std::make_pair( - MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()), - HashableDimensionKey(metricData.data(i).dimensions_in_condition())), - i)); + dimensionIndexMap.insert( + std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(), + metricData.data(i).dimensions_in_condition()), + i)); } for (const auto& itr : dimensionIndexMap) { *sortedMetricData->add_data() = metricData.data(itr.second); diff --git a/cmds/statsd/tools/Android.mk b/cmds/statsd/tools/Android.mk index faf2d2c7d4e7eefc9bf9bbc35d280d6e87e64f43..7253c9637026b930ba5246662dd1cfd1e712f35d 100644 --- a/cmds/statsd/tools/Android.mk +++ b/cmds/statsd/tools/Android.mk @@ -16,5 +16,5 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -# Include the sub-makefiles +#Include the sub-makefiles include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index a65095f4188d56ea00f2628d935dc2f8786c14ed..c7e4c7b717937c50cd92e8e387c15f8bc54a5791 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -24,7 +24,6 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \ statsdprotolite -LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_PRIVILEGED_MODULE := true LOCAL_DEX_PREOPT := false LOCAL_CERTIFICATE := platform diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml index cd76c9d379b5766c314a245ac5f32b47041a4c06..52673fbdcf92bf1881202167ad388c1232895e1d 100644 --- a/cmds/statsd/tools/dogfood/AndroidManifest.xml +++ b/cmds/statsd/tools/dogfood/AndroidManifest.xml @@ -37,5 +37,7 @@ + + diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml index 5d35c291f365cb44505270021fa0f08e096ef7d2..784ed40ce2c283a5abb2fc15c7be7219cfdf3e3e 100644 --- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml +++ b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml @@ -31,6 +31,18 @@ android:layout_height="wrap_content" android:background="@android:color/holo_green_light" android:text="@string/push_config"/> +