diff --git a/Android.bp b/Android.bp index 7f3243f13d197db3242b5057a2039bdd98fb6281..874c5a621549805988173b0a6a5591b48545f489 100755 --- a/Android.bp +++ b/Android.bp @@ -499,7 +499,7 @@ java_library { "telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl", "telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl", "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl", - "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl", + "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl", "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl", "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl", @@ -684,7 +684,8 @@ java_library { // Loaded with System.loadLibrary by android.view.textclassifier required: [ "libtextclassifier", - "libmedia2_jni",], + "libmedia2_jni", + ], javac_shard_size: 150, @@ -813,6 +814,7 @@ gensrcs { ], srcs: [ + "core/proto/android/os/backtrace.proto", "core/proto/android/os/batterytype.proto", "core/proto/android/os/cpufreq.proto", "core/proto/android/os/cpuinfo.proto", @@ -834,7 +836,7 @@ gensrcs { " -I . " + " $(in)", - output_extension = "proto.h", + output_extension: "proto.h", } subdirs = [ diff --git a/Android.mk b/Android.mk index 58e21ffd161770b40bd6a14fac2c89e79d76042c..bbe2faf2e9ed05dad1c8794a71c9bbc97049c372 100755 --- a/Android.mk +++ b/Android.mk @@ -143,7 +143,6 @@ framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \ bouncycastle \ okhttp \ ext \ - icu4j \ framework \ voip-common \ @@ -165,6 +164,8 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -knowntags ./frameworks/base/docs/knowntags.txt \ -knowntags ./libcore/known_oj_tags.txt \ -manifest ./frameworks/base/core/res/AndroidManifest.xml \ + -hidePackage com.android.internal \ + -hidePackage com.android.internal.util \ -hidePackage com.android.okhttp \ -hidePackage com.android.org.conscrypt \ -hidePackage com.android.server \ @@ -195,6 +196,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -since $(SRC_API_DIR)/25.txt 25 \ -since $(SRC_API_DIR)/26.txt 26 \ -since $(SRC_API_DIR)/27.txt 27 \ + -since ./frameworks/base/api/current.txt P \ -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \ -overview $(LOCAL_PATH)/core/java/overview.html \ @@ -331,8 +333,6 @@ LOCAL_DROIDDOC_OPTIONS:=\ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ -referenceonly \ -api $(INTERNAL_PLATFORM_API_FILE) \ - -privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \ - -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \ -removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \ -nodocs @@ -343,8 +343,7 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) $(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \ - $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \ - $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) + $(INTERNAL_PLATFORM_REMOVED_API_FILE) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) # ==== the system api stubs =================================== @@ -369,8 +368,6 @@ LOCAL_DROIDDOC_OPTIONS:=\ -referenceonly \ -showAnnotation android.annotation.SystemApi \ -api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \ - -privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \ - -privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \ -removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \ -exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \ -nodocs @@ -382,8 +379,8 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) $(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \ - $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \ - $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) + $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \ + $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE)) # ==== the test api stubs =================================== @@ -419,9 +416,43 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -$(INTERNAL_PLATFORM_TEST_API_FILE): $(full_target) +$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_TEST_API_FILE) \ + $(INTERNAL_PLATFORM_TEST_REMOVED_API_FILE) \ + $(INTERNAL_PLATFORM_TEST_EXACT_API_FILE) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE)) +# ==== the complete hidden api list =================================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES) +LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES) +LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) + +LOCAL_MODULE := hidden-api-list + +LOCAL_DROIDDOC_OPTIONS:=\ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -referenceonly \ + -showUnannotated \ + -showAnnotation android.annotation.SystemApi \ + -showAnnotation android.annotation.TestApi \ + -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \ + -nodocs + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk + +LOCAL_UNINSTALLABLE_MODULE := true + +include $(BUILD_DROIDDOC) + +$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) + # ==== check javadoc comments but don't generate docs ======== include $(CLEAR_VARS) @@ -664,6 +695,7 @@ LOCAL_DROIDDOC_OPTIONS:= \ -toroot / \ -hdf android.whichdoc online \ -devsite \ + -yamlV2 \ $(sample_groups) \ -hdf android.hasSamples true \ -samplesdir $(samples_dir) diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk index b7b87dda1957f4534e50f7cd71f9343a131c6360..6156a0c1f8e8e87a6cb3316fd5e2452ae9c531cc 100644 --- a/apct-tests/perftests/core/Android.mk +++ b/apct-tests/perftests/core/Android.mk @@ -16,6 +16,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_JAVA_LIBRARIES := android.test.base LOCAL_PACKAGE_NAME := CorePerfTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55b97e7675fd246f80b55101832cd35b0dfa29e7 --- /dev/null +++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java @@ -0,0 +1,370 @@ +/* + * 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.widget; + +import static android.view.View.MeasureSpec.AT_MOST; +import static android.view.View.MeasureSpec.EXACTLY; +import static android.view.View.MeasureSpec.UNSPECIFIED; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Typeface; +import android.graphics.Canvas; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; +import android.text.PrecomputedText; +import android.text.Layout; +import android.text.BoringLayout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.style.TextAppearanceSpan; +import android.view.LayoutInflater; +import android.text.TextPerfUtils; +import android.view.View.MeasureSpec; +import android.view.DisplayListCanvas; +import android.view.RenderNode; + +import com.android.perftests.core.R; + +import java.util.Random; +import java.util.Locale; + +import org.junit.Before; +import org.junit.Test; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertTrue; + +import static android.widget.TextView.UNKNOWN_BORING; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class TextViewPrecomputedTextPerfTest { + 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 final boolean STYLE_TEXT = true; + + private static TextPaint PAINT = new TextPaint(); + private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); + + public TextViewPrecomputedTextPerfTest() {} + + private static class TestableTextView extends TextView { + public TestableTextView(Context ctx) { + super(ctx); + } + + public void onMeasure(int w, int h) { + super.onMeasure(w, h); + } + + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + } + } + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private TextPerfUtils mTextUtil = new TextPerfUtils(); + + @Before + public void setUp() { + mTextUtil.resetRandom(0 /* seed */); + } + + private static Context getContext() { + return InstrumentationRegistry.getTargetContext(); + } + + @Test + public void testNewLayout_RandomText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BoringLayout.Metrics metrics = new BoringLayout.Metrics(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final TextView textView = new TextView(getContext()); + textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED); + textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); + textView.setText(text); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING, + TEXT_WIDTH, false); + } + } + + @Test + public void testNewLayout_PrecomputedText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BoringLayout.Metrics metrics = new BoringLayout.Metrics(); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TextView textView = new TextView(getContext()); + textView.setTextMetricsParams(params); + textView.setText(text); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING, + TEXT_WIDTH, false); + } + } + + @Test + public void testNewLayout_PrecomputedText_Selectable() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BoringLayout.Metrics metrics = new BoringLayout.Metrics(); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TextView textView = new TextView(getContext()); + textView.setTextIsSelectable(true); + textView.setTextMetricsParams(params); + textView.setText(text); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING, + TEXT_WIDTH, false); + } + } + + @Test + public void testSetText_RandomText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BoringLayout.Metrics metrics = new BoringLayout.Metrics(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final TextView textView = new TextView(getContext()); + textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED); + textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.setText(text); + } + } + + @Test + public void testSetText_PrecomputedText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BoringLayout.Metrics metrics = new BoringLayout.Metrics(); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TextView textView = new TextView(getContext()); + textView.setTextMetricsParams(params); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.setText(text); + } + } + + @Test + public void testSetText_PrecomputedText_Selectable() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BoringLayout.Metrics metrics = new BoringLayout.Metrics(); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TextView textView = new TextView(getContext()); + textView.setTextIsSelectable(true); + textView.setTextMetricsParams(params); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.setText(text); + } + } + + @Test + public void testOnMeasure_RandomText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH); + int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final TestableTextView textView = new TestableTextView(getContext()); + textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED); + textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); + textView.setText(text); + textView.nullLayouts(); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.onMeasure(width, height); + } + } + + @Test + public void testOnMeasure_PrecomputedText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH); + int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TestableTextView textView = new TestableTextView(getContext()); + textView.setTextMetricsParams(params); + textView.setText(text); + textView.nullLayouts(); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.onMeasure(width, height); + } + } + + @Test + public void testOnMeasure_PrecomputedText_Selectable() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH); + int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TestableTextView textView = new TestableTextView(getContext()); + textView.setTextIsSelectable(true); + textView.setTextMetricsParams(params); + textView.setText(text); + textView.nullLayouts(); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.onMeasure(width, height); + } + } + + @Test + public void testOnDraw_RandomText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH); + int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final TestableTextView textView = new TestableTextView(getContext()); + textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED); + textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); + textView.setText(text); + textView.nullLayouts(); + textView.onMeasure(width, height); + final DisplayListCanvas c = node.start( + textView.getMeasuredWidth(), textView.getMeasuredHeight()); + textView.nullLayouts(); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.onDraw(c); + } + } + + @Test + public void testOnDraw_PrecomputedText() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH); + int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TestableTextView textView = new TestableTextView(getContext()); + textView.setTextMetricsParams(params); + textView.setText(text); + textView.nullLayouts(); + textView.onMeasure(width, height); + final DisplayListCanvas c = node.start( + textView.getMeasuredWidth(), textView.getMeasuredHeight()); + textView.nullLayouts(); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.onDraw(c); + } + } + + @Test + public void testOnDraw_PrecomputedText_Selectable() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH); + int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build(); + final CharSequence text = PrecomputedText.create( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params); + final TestableTextView textView = new TestableTextView(getContext()); + textView.setTextIsSelectable(true); + textView.setTextMetricsParams(params); + textView.setText(text); + textView.nullLayouts(); + textView.onMeasure(width, height); + final DisplayListCanvas c = node.start( + textView.getMeasuredWidth(), textView.getMeasuredHeight()); + textView.nullLayouts(); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + textView.onDraw(c); + } + } +} diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk index 2db0dd65b05f8da125f8d8de1d35b708c5d154b3..a803369d1e03a74ae508cf612a4562a709808392 100644 --- a/apct-tests/perftests/multiuser/Android.mk +++ b/apct-tests/perftests/multiuser/Android.mk @@ -24,6 +24,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ ub-uiautomator LOCAL_PACKAGE_NAME := MultiUserPerfTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform diff --git a/api/current.txt b/api/current.txt index fff502a577db85abf992a9620a93c5b8375493e8..8efc7044af5fb317751afc876d65cb178a815bcc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36,7 +36,6 @@ package android { field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; - field public static final java.lang.String BIND_SLICE = "android.permission.BIND_SLICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; @@ -73,6 +72,7 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; + field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; @@ -1378,6 +1378,7 @@ package android { field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e field public static final int textEditSuggestionItemLayout = 16843636; // 0x1010374 field public static final int textFilterEnabled = 16843007; // 0x10100ff + field public static final int textFontWeight = 16844166; // 0x1010586 field public static final int textIsSelectable = 16843542; // 0x1010316 field public static final int textOff = 16843045; // 0x1010125 field public static final int textOn = 16843044; // 0x1010124 @@ -1548,6 +1549,7 @@ package android { field public static final int windowHideAnimation = 16842935; // 0x10100b7 field public static final int windowIsFloating = 16842839; // 0x1010057 field public static final int windowIsTranslucent = 16842840; // 0x1010058 + field public static final int windowLayoutInDisplayCutoutMode = 16844167; // 0x1010587 field public static final int windowLightNavigationBar = 16844140; // 0x101056c field public static final int windowLightStatusBar = 16844000; // 0x10104e0 field public static final int windowMinWidthMajor = 16843606; // 0x1010356 @@ -3882,6 +3884,7 @@ package android.app { method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException; method public deprecated java.util.List getRunningServices(int) throws java.lang.SecurityException; method public deprecated java.util.List getRunningTasks(int) throws java.lang.SecurityException; + method public boolean isBackgroundRestricted(); method public deprecated boolean isInLockTaskMode(); method public boolean isLowRamDevice(); method public static boolean isRunningInTestHarness(); @@ -5741,15 +5744,23 @@ package android.app { field public static final int PRIORITY_CATEGORY_ALARMS = 32; // 0x20 field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8 field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2 - field public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 64; // 0x40 + field public static final int PRIORITY_CATEGORY_MEDIA = 64; // 0x40 field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4 field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1 field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10 + field public static final int PRIORITY_CATEGORY_SYSTEM = 128; // 0x80 field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0 field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1 field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2 - field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 - field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_AMBIENT = 128; // 0x80 + field public static final int SUPPRESSED_EFFECT_BADGE = 64; // 0x40 + field public static final int SUPPRESSED_EFFECT_FULL_SCREEN_INTENT = 4; // 0x4 + field public static final int SUPPRESSED_EFFECT_LIGHTS = 8; // 0x8 + field public static final int SUPPRESSED_EFFECT_NOTIFICATION_LIST = 256; // 0x100 + field public static final int SUPPRESSED_EFFECT_PEEK = 16; // 0x10 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_STATUS_BAR = 32; // 0x20 field public final int priorityCallSenders; field public final int priorityCategories; field public final int priorityMessageSenders; @@ -6702,11 +6713,6 @@ package android.app.admin { field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 - field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4 - field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2 - 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 @@ -7173,14 +7179,15 @@ package android.app.slice { method public java.util.List getItems(); method public android.app.slice.SliceSpec getSpec(); method public android.net.Uri getUri(); + method public boolean isCallerNeeded(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final java.lang.String EXTRA_RANGE_VALUE = "android.app.slice.extra.RANGE_VALUE"; field public static final deprecated java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE"; field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE"; field public static final java.lang.String HINT_ACTIONS = "actions"; - field public static final java.lang.String HINT_CALLER_NEEDED = "caller_needed"; field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; + field public static final java.lang.String HINT_KEY_WORDS = "key_words"; field public static final java.lang.String HINT_LARGE = "large"; field public static final java.lang.String HINT_LIST = "list"; field public static final java.lang.String HINT_LIST_ITEM = "list_item"; @@ -7225,6 +7232,7 @@ package android.app.slice { method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List); method public android.app.slice.Slice build(); + method public android.app.slice.Slice.Builder setCallerNeeded(boolean); method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec); } @@ -7261,19 +7269,11 @@ package android.app.slice { method public java.util.Collection getSliceDescendants(android.net.Uri); method public android.net.Uri mapIntentToUri(android.content.Intent); method public void pinSlice(android.net.Uri, java.util.List); - method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List); - method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List, java.util.concurrent.Executor); - method public void registerSliceCallback(android.net.Uri, java.util.List, android.app.slice.SliceManager.SliceCallback); - 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 CATEGORY_SLICE = "android.app.slice.category.SLICE"; field public static final java.lang.String SLICE_METADATA_KEY = "android.metadata.SLICE_URI"; } - public static abstract interface SliceManager.SliceCallback { - method public abstract void onSliceUpdated(android.app.slice.Slice); - } - public abstract class SliceProvider extends android.content.ContentProvider { ctor public SliceProvider(); method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); @@ -7446,6 +7446,7 @@ package android.app.usage { method public java.util.Map queryAndAggregateUsageStats(long, long); method public java.util.List queryConfigurations(int, long, long); method public android.app.usage.UsageEvents queryEvents(long, long); + method public android.app.usage.UsageEvents queryEventsForSelf(long, long); method public java.util.List queryUsageStats(int, long, long); field public static final int INTERVAL_BEST = 4; // 0x4 field public static final int INTERVAL_DAILY = 0; // 0x0 @@ -8972,6 +8973,7 @@ package android.content { public class ClipboardManager extends android.text.ClipboardManager { method public void addPrimaryClipChangedListener(android.content.ClipboardManager.OnPrimaryClipChangedListener); + method public void clearPrimaryClip(); method public android.content.ClipData getPrimaryClip(); method public android.content.ClipDescription getPrimaryClipDescription(); method public deprecated java.lang.CharSequence getText(); @@ -13595,7 +13597,7 @@ package android.graphics { } public class EmbossMaskFilter extends android.graphics.MaskFilter { - ctor public EmbossMaskFilter(float[], float, float, float); + ctor public deprecated EmbossMaskFilter(float[], float, float, float); } public final class ImageDecoder implements java.lang.AutoCloseable { @@ -13607,17 +13609,25 @@ package android.graphics { method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source) throws java.io.IOException; method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException; method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source) throws java.io.IOException; + method public int getAllocator(); + method public boolean getConserveMemory(); + method public android.graphics.Rect getCrop(); + method public boolean getDecodeAsAlphaMask(); + method public boolean getMutable(); + method public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener(); + method public android.graphics.PostProcessor getPostProcessor(); + method public boolean getRequireUnpremultiplied(); method public android.util.Size getSampledSize(int); - method public void setAllocator(int); - method public void setAsAlphaMask(boolean); - method public void setCrop(android.graphics.Rect); - method public void setMutable(boolean); - method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); - method public void setPostProcessor(android.graphics.PostProcessor); - method public void setPreferRamOverQuality(boolean); - method public void setRequireUnpremultiplied(boolean); - method public void setResize(int, int); - method public void setResize(int); + method public android.graphics.ImageDecoder setAllocator(int); + method public android.graphics.ImageDecoder setConserveMemory(boolean); + method public android.graphics.ImageDecoder setCrop(android.graphics.Rect); + method public android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean); + method public android.graphics.ImageDecoder setMutable(boolean); + method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); + method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor); + method public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean); + method public android.graphics.ImageDecoder setResize(int, int); + method public android.graphics.ImageDecoder setResize(int); field public static final int ALLOCATOR_DEFAULT = 0; // 0x0 field public static final int ALLOCATOR_HARDWARE = 3; // 0x3 field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2 @@ -14510,18 +14520,18 @@ 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 int getRepeatCount(); method public final boolean isAutoMirrored(); method public boolean isRunning(); method public void registerAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback); method public void setAlpha(int); method public void setColorFilter(android.graphics.ColorFilter); - method public void setLoopCount(int); + method public void setRepeatCount(int); method public void start(); method public void stop(); method public boolean unregisterAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback); - field public static final int LOOP_INFINITE = -1; // 0xffffffff + field public static final int REPEAT_INFINITE = -1; // 0xffffffff } public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable { @@ -14819,6 +14829,10 @@ package android.graphics.drawable { method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int); method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int); method public int describeContents(); + method public int getResId(); + method public java.lang.String getResPackage(); + method public int getType(); + method public android.net.Uri getUri(); method public android.graphics.drawable.Drawable loadDrawable(android.content.Context); method public void loadDrawableAsync(android.content.Context, android.os.Message); method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler); @@ -14827,6 +14841,11 @@ package android.graphics.drawable { method public android.graphics.drawable.Icon setTintMode(android.graphics.PorterDuff.Mode); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5 + field public static final int TYPE_BITMAP = 1; // 0x1 + field public static final int TYPE_DATA = 3; // 0x3 + field public static final int TYPE_RESOURCE = 2; // 0x2 + field public static final int TYPE_URI = 4; // 0x4 } public static abstract interface Icon.OnDrawableLoadedListener { @@ -21206,7 +21225,7 @@ package android.inputmethodservice { method public void onWindowHidden(); method public void onWindowShown(); method public void requestHideSelf(int); - method public void requestShowSelf(int); + method public final void requestShowSelf(int); method public boolean sendDefaultEditorAction(boolean); method public void sendDownUpKeyEvents(int); method public void sendKeyChar(char); @@ -21215,15 +21234,14 @@ package android.inputmethodservice { method public void setCandidatesViewShown(boolean); method public void setExtractView(android.view.View); method public void setExtractViewShown(boolean); - method public void setInputMethod(java.lang.String); - method public void setInputMethodAndSubtype(java.lang.String, android.view.inputmethod.InputMethodSubtype); method public void setInputView(android.view.View); - method public boolean shouldOfferSwitchingToNextInputMethod(); + method public final boolean shouldOfferSwitchingToNextInputMethod(); method public void showStatusIcon(int); method public void showWindow(boolean); method public void switchInputMethod(java.lang.String); - method public boolean switchToLastInputMethod(); - method public boolean switchToNextInputMethod(boolean); + method public final void switchInputMethod(java.lang.String, android.view.inputmethod.InputMethodSubtype); + method public final boolean switchToNextInputMethod(boolean); + method public final boolean switchToPreviousInputMethod(); method public void updateFullscreenMode(); method public void updateInputViewShown(); field public static final int BACK_DISPOSITION_DEFAULT = 0; // 0x0 @@ -22520,8 +22538,8 @@ package android.media { method public java.io.FileDescriptor getFileDescriptor(); method public long getFileDescriptorLength(); method public long getFileDescriptorOffset(); - method public long getId(); method public android.media.Media2DataSource getMedia2DataSource(); + method public java.lang.String getMediaId(); method public long getStartPosition(); method public int getType(); method public android.net.Uri getUri(); @@ -22545,7 +22563,7 @@ package android.media { method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri); method public android.media.DataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map, java.util.List); method public android.media.DataSourceDesc.Builder setEndPosition(long); - method public android.media.DataSourceDesc.Builder setId(long); + method public android.media.DataSourceDesc.Builder setMediaId(java.lang.String); method public android.media.DataSourceDesc.Builder setStartPosition(long); } @@ -22750,6 +22768,7 @@ package android.media { method public abstract void close(); method public android.graphics.Rect getCropRect(); method public abstract int getFormat(); + method public android.hardware.HardwareBuffer getHardwareBuffer(); method public abstract int getHeight(); method public abstract android.media.Image.Plane[] getPlanes(); method public abstract long getTimestamp(); @@ -22855,12 +22874,12 @@ package android.media { 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); + method public void onChildrenChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle); + method public void onGetChildrenDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List, android.os.Bundle); + method public void onGetItemDone(android.media.MediaBrowser2, java.lang.String, android.media.MediaItem2); + method public void onGetLibraryRootDone(android.media.MediaBrowser2, android.os.Bundle, java.lang.String, android.os.Bundle); + method public void onGetSearchResultDone(android.media.MediaBrowser2, java.lang.String, int, int, java.util.List, android.os.Bundle); + method public void onSearchResultChanged(android.media.MediaBrowser2, java.lang.String, int, android.os.Bundle); } public final class MediaCas implements java.lang.AutoCloseable { @@ -23346,15 +23365,17 @@ package android.media { method public void close(); method public void fastForward(); method public long getBufferedPosition(); - method public android.media.MediaItem2 getCurrentPlaylistItem(); + method public android.media.MediaItem2 getCurrentMediaItem(); 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 android.media.MediaMetadata2 getPlaylistMetadata(); method public long getPosition(); + method public int getRepeatMode(); method public android.app.PendingIntent getSessionActivity(); method public android.media.SessionToken2 getSessionToken(); + method public int getShuffleMode(); method public boolean isConnected(); method public void pause(); method public void play(); @@ -23366,34 +23387,41 @@ package android.media { 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 replacePlaylistItem(int, 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 setPlaybackSpeed(float); + method public void setPlaylist(java.util.List, android.media.MediaMetadata2); method public void setRating(java.lang.String, android.media.Rating2); + method public void setRepeatMode(int); + method public void setShuffleMode(int); method public void setVolumeTo(int, int); - method public void skipToNext(); + method public void skipToNextItem(); method public void skipToPlaylistItem(android.media.MediaItem2); - method public void skipToPrevious(); + method public void skipToPreviousItem(); method public void stop(); + method public void updatePlaylistMetadata(android.media.MediaMetadata2); } 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); + method public void onAllowedCommandsChanged(android.media.MediaController2, android.media.MediaSession2.CommandGroup); + method public void onBufferedPositionChanged(android.media.MediaController2, long); + method public void onConnected(android.media.MediaController2, android.media.MediaSession2.CommandGroup); + method public void onCurrentMediaItemChanged(android.media.MediaController2, android.media.MediaItem2); + method public void onCustomCommand(android.media.MediaController2, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver); + method public void onCustomLayoutChanged(android.media.MediaController2, java.util.List); + method public void onDisconnected(android.media.MediaController2); + method public void onError(android.media.MediaController2, int, android.os.Bundle); + method public void onPlaybackInfoChanged(android.media.MediaController2, android.media.MediaController2.PlaybackInfo); + method public void onPlaybackSpeedChanged(android.media.MediaController2, float); + method public void onPlayerStateChanged(android.media.MediaController2, int); + method public void onPlaylistChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, java.util.List, android.media.MediaMetadata2); + method public void onPlaylistMetadataChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2); + method public void onPositionChanged(android.media.MediaController2, long, long); + method public void onRepeatModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int); + method public void onShuffleModeChanged(android.media.MediaController2, android.media.MediaPlaylistAgent, int); } public static final class MediaController2.PlaybackInfo { @@ -23432,6 +23460,11 @@ package android.media { method protected void finalize(); method public boolean requiresSecureDecoderComponent(java.lang.String); method public void setMediaCasSession(android.media.MediaCas.Session); + field public static final byte SCRAMBLE_CONTROL_EVEN_KEY = 2; // 0x2 + field public static final byte SCRAMBLE_CONTROL_ODD_KEY = 3; // 0x3 + field public static final byte SCRAMBLE_CONTROL_RESERVED = 1; // 0x1 + field public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = 0; // 0x0 + field public static final byte SCRAMBLE_FLAG_PES_HEADER = 1; // 0x1 } public class MediaDescription implements android.os.Parcelable { @@ -23845,9 +23878,11 @@ package android.media { } 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); + ctor public MediaLibraryService2.MediaLibrarySession.Builder(android.media.MediaLibraryService2, 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 setPlayer(android.media.MediaPlayerBase); + method public android.media.MediaLibraryService2.MediaLibrarySession.Builder setPlaylistAgent(android.media.MediaPlaylistAgent); 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); @@ -23855,13 +23890,13 @@ package android.media { 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); + method public java.util.List onGetChildren(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle); + method public android.media.MediaItem2 onGetItem(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String); + method public android.media.MediaLibraryService2.LibraryRoot onGetLibraryRoot(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, android.os.Bundle); + method public java.util.List onGetSearchResult(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, int, int, android.os.Bundle); + method public void onSearch(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onSubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onUnsubscribe(android.media.MediaLibraryService2.MediaLibrarySession, android.media.MediaSession2.ControllerInfo, java.lang.String); } public final class MediaMetadata implements android.os.Parcelable { @@ -24299,68 +24334,74 @@ package android.media { field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1 } - public abstract class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable { - method public abstract void addPlaylistItem(int, android.media.DataSourceDesc); + public abstract class MediaPlayer2 extends android.media.MediaPlayerBase implements android.media.AudioRouting { method public abstract void attachAuxEffect(int); + method public abstract void clearDrmEventCallback(); + method public abstract void clearMediaPlayer2EventCallback(); method public abstract void clearPendingCommands(); method public abstract void close(); 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 long getBufferedPosition(); method public abstract long getCurrentPosition(); method public abstract android.media.MediaPlayer2.DrmInfo getDrmInfo(); + method public abstract android.media.MediaDrm.KeyRequest getDrmKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map) throws android.media.MediaPlayer2.NoDrmSchemeException; method public abstract java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException; 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 int getMediaPlayer2State(); method public abstract android.os.PersistableBundle getMetrics(); method public abstract android.media.PlaybackParams getPlaybackParams(); - method public abstract java.util.List getPlaylist(); method public abstract int getSelectedTrack(int); method public abstract android.media.SyncParams getSyncParams(); method public abstract android.media.MediaTimestamp getTimestamp(); method public abstract java.util.List getTrackInfo(); method public abstract int getVideoHeight(); method public abstract int getVideoWidth(); - method public abstract boolean isPlaying(); - method public abstract void movePlaylistItem(int, int); - method public abstract void pause(); - method public abstract void play(); - method public abstract void prepareAsync(); + method public void notifyWhenCommandLabelReached(java.lang.Object); method public abstract void prepareDrm(java.util.UUID) throws android.media.MediaPlayer2.ProvisioningNetworkErrorException, android.media.MediaPlayer2.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; - method public abstract byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException; - method public abstract void registerDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback); - method public abstract void registerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.EventCallback); + method public abstract byte[] provideDrmKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException; method public abstract void releaseDrm() throws android.media.MediaPlayer2.NoDrmSchemeException; - method public abstract android.media.DataSourceDesc removePlaylistItem(int); - method public abstract void reset(); - method public abstract void restoreKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException; + method public abstract void restoreDrmKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException; + method public void seekTo(long); method public abstract void seekTo(long, int); method public abstract void selectTrack(int); - method public abstract void setAudioAttributes(android.media.AudioAttributes); method public abstract void setAudioSessionId(int); method public abstract void setAuxEffectSendLevel(float); - method public abstract void setCurrentPlaylistItem(int); - method public abstract void setDataSource(android.media.DataSourceDesc) throws java.io.IOException; + method public abstract void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback); method public abstract void setDrmPropertyString(java.lang.String, java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException; - method public abstract void setLoopingMode(int); - method public abstract void setNextPlaylistItem(int); + method public abstract void setMediaPlayer2EventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.MediaPlayer2EventCallback); method public abstract void setOnDrmConfigHelper(android.media.MediaPlayer2.OnDrmConfigHelper); method public abstract void setPlaybackParams(android.media.PlaybackParams); - method public abstract void setPlaylist(java.util.List, int) throws java.io.IOException; method public abstract void setSurface(android.view.Surface); method public abstract void setSyncParams(android.media.SyncParams); - method public abstract void setVolume(float, float); - method public abstract void unregisterDrmEventCallback(android.media.MediaPlayer2.DrmEventCallback); - method public abstract void unregisterEventCallback(android.media.MediaPlayer2.EventCallback); - field public static final int LOOPING_MODE_FULL = 1; // 0x1 - field public static final int LOOPING_MODE_NONE = 0; // 0x0 - field public static final int LOOPING_MODE_SHUFFLE = 3; // 0x3 - field public static final int LOOPING_MODE_SINGLE = 2; // 0x2 + field public static final int MEDIAPLAYER2_STATE_ERROR = 5; // 0x5 + field public static final int MEDIAPLAYER2_STATE_IDLE = 1; // 0x1 + field public static final int MEDIAPLAYER2_STATE_PAUSED = 3; // 0x3 + field public static final int MEDIAPLAYER2_STATE_PLAYING = 4; // 0x4 + field public static final int MEDIAPLAYER2_STATE_PREPARED = 2; // 0x2 + field public static final int MEDIA_CALL_ATTACH_AUX_EFFECT = 1; // 0x1 + field public static final int MEDIA_CALL_DESELECT_TRACK = 2; // 0x2 + field public static final int MEDIA_CALL_LOOP_CURRENT = 3; // 0x3 + field public static final int MEDIA_CALL_PAUSE = 4; // 0x4 + field public static final int MEDIA_CALL_PLAY = 5; // 0x5 + field public static final int MEDIA_CALL_PREPARE = 6; // 0x6 + field public static final int MEDIA_CALL_RELEASE_DRM = 12; // 0xc + field public static final int MEDIA_CALL_RESTORE_DRM_KEYS = 13; // 0xd + field public static final int MEDIA_CALL_SEEK_TO = 14; // 0xe + field public static final int MEDIA_CALL_SELECT_TRACK = 15; // 0xf + field public static final int MEDIA_CALL_SET_AUDIO_ATTRIBUTES = 16; // 0x10 + field public static final int MEDIA_CALL_SET_AUDIO_SESSION_ID = 17; // 0x11 + field public static final int MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12 + field public static final int MEDIA_CALL_SET_DATA_SOURCE = 19; // 0x13 + field public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCE = 22; // 0x16 + field public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCES = 23; // 0x17 + field public static final int MEDIA_CALL_SET_PLAYBACK_PARAMS = 24; // 0x18 + field public static final int MEDIA_CALL_SET_PLAYBACK_SPEED = 25; // 0x19 + field public static final int MEDIA_CALL_SET_PLAYER_VOLUME = 26; // 0x1a + field public static final int MEDIA_CALL_SET_SURFACE = 27; // 0x1b + field public static final int MEDIA_CALL_SET_SYNC_PARAMS = 28; // 0x1c + field public static final int MEDIA_CALL_SKIP_TO_NEXT = 29; // 0x1d field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14 field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11 field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8 @@ -24372,9 +24413,7 @@ package android.media { field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320 field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd - field public static final int MEDIA_INFO_COMPLETE_CALL_PAUSE = 102; // 0x66 - field public static final int MEDIA_INFO_COMPLETE_CALL_PLAY = 101; // 0x65 - field public static final int MEDIA_INFO_COMPLETE_CALL_SEEK = 103; // 0x67 + field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0 field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322 field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321 field public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5; // 0x5 @@ -24400,8 +24439,8 @@ package android.media { public static abstract class MediaPlayer2.DrmEventCallback { ctor public MediaPlayer2.DrmEventCallback(); - method public void onDrmInfo(android.media.MediaPlayer2, long, android.media.MediaPlayer2.DrmInfo); - method public void onDrmPrepared(android.media.MediaPlayer2, long, int); + method public void onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo); + method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int); } public static abstract class MediaPlayer2.DrmInfo { @@ -24410,13 +24449,15 @@ package android.media { method public abstract java.util.List getSupportedSchemes(); } - public static abstract class MediaPlayer2.EventCallback { - ctor public MediaPlayer2.EventCallback(); - method public void onBufferingUpdate(android.media.MediaPlayer2, long, int); - method public void onError(android.media.MediaPlayer2, long, int, int); - method public void onInfo(android.media.MediaPlayer2, long, int, int); - method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, long, android.media.TimedMetaData); - method public void onVideoSizeChanged(android.media.MediaPlayer2, long, int, int); + public static abstract class MediaPlayer2.MediaPlayer2EventCallback { + ctor public MediaPlayer2.MediaPlayer2EventCallback(); + method public void onCallComplete(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int); + method public void onCommandLabelReached(android.media.MediaPlayer2, java.lang.Object); + method public void onError(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int); + method public void onInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int); + method public void onMediaTimeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp); + method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData); + method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int); } public static final class MediaPlayer2.MetricsConstants { @@ -24439,7 +24480,7 @@ package android.media { } public static abstract interface MediaPlayer2.OnDrmConfigHelper { - method public abstract void onDrmConfig(android.media.MediaPlayer2, long); + method public abstract void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc); } public static abstract class MediaPlayer2.ProvisioningNetworkErrorException extends android.media.MediaDrmException { @@ -24466,14 +24507,87 @@ package android.media { public abstract class MediaPlayerBase implements java.lang.AutoCloseable { ctor public MediaPlayerBase(); method public abstract android.media.AudioAttributes getAudioAttributes(); + method public long getBufferedPosition(); + method public abstract int getBufferingState(); + method public abstract android.media.DataSourceDesc getCurrentDataSource(); + method public long getCurrentPosition(); + method public long getDuration(); + method public float getMaxPlayerVolume(); + method public float getPlaybackSpeed(); method public abstract int getPlayerState(); + method public abstract float getPlayerVolume(); + method public boolean isReversePlaybackSupported(); + method public abstract void loopCurrent(boolean); method public abstract void pause(); method public abstract void play(); + method public abstract void prepare(); + method public abstract void registerPlayerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayerBase.PlayerEventCallback); + method public abstract void reset(); + method public abstract void seekTo(long); 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 + method public abstract void setDataSource(android.media.DataSourceDesc); + method public abstract void setNextDataSource(android.media.DataSourceDesc); + method public abstract void setNextDataSources(java.util.List); + method public abstract void setPlaybackSpeed(float); + method public abstract void setPlayerVolume(float); + method public abstract void skipToNext(); + method public abstract void unregisterPlayerEventCallback(android.media.MediaPlayerBase.PlayerEventCallback); + field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1 + field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2 + field public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3; // 0x3 + field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0 + field public static final int PLAYER_STATE_ERROR = 3; // 0x3 + field public static final int PLAYER_STATE_IDLE = 0; // 0x0 + field public static final int PLAYER_STATE_PAUSED = 1; // 0x1 + field public static final int PLAYER_STATE_PLAYING = 2; // 0x2 + field public static final long UNKNOWN_TIME = -1L; // 0xffffffffffffffffL + } + + public static abstract class MediaPlayerBase.PlayerEventCallback { + ctor public MediaPlayerBase.PlayerEventCallback(); + method public void onBufferingStateChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc, int); + method public void onCurrentDataSourceChanged(android.media.MediaPlayerBase, android.media.DataSourceDesc); + method public void onMediaPrepared(android.media.MediaPlayerBase, android.media.DataSourceDesc); + method public void onPlayerStateChanged(android.media.MediaPlayerBase, int); + } + + public abstract class MediaPlaylistAgent { + ctor public MediaPlaylistAgent(android.content.Context); + method public void addPlaylistItem(int, android.media.MediaItem2); + method public java.util.List getPlaylist(); + method public android.media.MediaMetadata2 getPlaylistMetadata(); + method public int getRepeatMode(); + method public int getShuffleMode(); + method public final void notifyPlaylistChanged(); + method public final void notifyPlaylistMetadataChanged(); + method public final void notifyRepeatModeChanged(); + method public final void notifyShuffleModeChanged(); + method public final void registerPlaylistEventCallback(java.util.concurrent.Executor, android.media.MediaPlaylistAgent.PlaylistEventCallback); + method public void removePlaylistItem(android.media.MediaItem2); + method public void replacePlaylistItem(int, android.media.MediaItem2); + method public void setPlaylist(java.util.List, android.media.MediaMetadata2); + method public void setRepeatMode(int); + method public void setShuffleMode(int); + method public void skipToNextItem(); + method public void skipToPlaylistItem(android.media.MediaItem2); + method public void skipToPreviousItem(); + method public final void unregisterPlaylistEventCallback(android.media.MediaPlaylistAgent.PlaylistEventCallback); + method public void updatePlaylistMetadata(android.media.MediaMetadata2); + 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 MediaPlaylistAgent.PlaylistEventCallback { + ctor public MediaPlaylistAgent.PlaylistEventCallback(); + method public void onPlaylistChanged(android.media.MediaPlaylistAgent, java.util.List, android.media.MediaMetadata2); + method public void onPlaylistMetadataChanged(android.media.MediaPlaylistAgent, android.media.MediaMetadata2); + method public void onRepeatModeChanged(android.media.MediaPlaylistAgent, int); + method public void onShuffleModeChanged(android.media.MediaPlaylistAgent, int); } public class MediaRecorder implements android.media.AudioRouting { @@ -24754,57 +24868,73 @@ package android.media { public class MediaSession2 implements java.lang.AutoCloseable { method public void addPlaylistItem(int, android.media.MediaItem2); + method public void clearOnDataSourceMissingHelper(); 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.MediaItem2 getCurrentMediaItem(); + method public float getPlaybackSpeed(); method public android.media.MediaPlayerBase getPlayer(); method public java.util.List getPlaylist(); - method public android.media.MediaSession2.PlaylistParams getPlaylistParams(); + method public android.media.MediaPlaylistAgent getPlaylistAgent(); + method public android.media.MediaMetadata2 getPlaylistMetadata(); + method public int getRepeatMode(); + method public int getShuffleMode(); method public android.media.SessionToken2 getToken(); - method public void notifyError(int, int); + method public android.media.VolumeProvider2 getVolumeProvider(); + method public void notifyError(int, android.os.Bundle); method public void pause(); method public void play(); method public void prepare(); method public void removePlaylistItem(android.media.MediaItem2); + method public void replacePlaylistItem(int, 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 setAudioFocusRequest(android.media.AudioFocusRequest); 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 setOnDataSourceMissingHelper(android.media.MediaSession2.OnDataSourceMissingHelper); + method public void setPlaybackSpeed(float); + method public void setPlaylist(java.util.List, android.media.MediaMetadata2); + method public void setRepeatMode(int); + method public void setShuffleMode(int); + method public void skipToNextItem(); method public void skipToPlaylistItem(android.media.MediaItem2); - method public void skipToPrevious(); + method public void skipToPreviousItem(); method public void stop(); - field public static final int COMMAND_CODE_BROWSER = 22; // 0x16 + method public void updatePlayer(android.media.MediaPlayerBase, android.media.MediaPlaylistAgent, android.media.VolumeProvider2); + method public void updatePlaylistMetadata(android.media.MediaMetadata2); + field public static final int COMMAND_CODE_BROWSER = 28; // 0x1c field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0 + field public static final int COMMAND_CODE_PLAYBACK_ADJUST_VOLUME = 11; // 0xb 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_SET_VOLUME = 10; // 0xa 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 COMMAND_CODE_PLAYLIST_ADD_ITEM = 15; // 0xf + field public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18; // 0x12 + field public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20; // 0x14 + field public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16; // 0x10 + field public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17; // 0x11 + field public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19; // 0x13 + field public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21; // 0x15 + field public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14; // 0xe + field public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13; // 0xd + field public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12; // 0xc + field public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 22; // 0x16 + field public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 24; // 0x18 + field public static final int COMMAND_CODE_PLAY_FROM_URI = 23; // 0x17 + field public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 25; // 0x19 + field public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 27; // 0x1b + field public static final int COMMAND_CODE_PREPARE_FROM_URI = 26; // 0x1a 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 @@ -24821,9 +24951,11 @@ package android.media { } public static final class MediaSession2.Builder { - ctor public MediaSession2.Builder(android.content.Context, android.media.MediaPlayerBase); + ctor public MediaSession2.Builder(android.content.Context); method public android.media.MediaSession2 build(); method public android.media.MediaSession2.Builder setId(java.lang.String); + method public android.media.MediaSession2.Builder setPlayer(android.media.MediaPlayerBase); + method public android.media.MediaSession2.Builder setPlaylistAgent(android.media.MediaPlaylistAgent); 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); @@ -24834,13 +24966,13 @@ package android.media { 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(); + method public android.os.Bundle getExtras(); } 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 android.os.Bundle getExtras(); method public int getIconResId(); method public boolean isEnabled(); } @@ -24851,7 +24983,7 @@ package android.media { 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 setExtras(android.os.Bundle); method public android.media.MediaSession2.CommandButton.Builder setIconResId(int); } @@ -24860,6 +24992,7 @@ package android.media { 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 java.util.List getCommands(); method public boolean hasCommand(android.media.MediaSession2.Command); method public boolean hasCommand(int); method public void removeCommand(android.media.MediaSession2.Command); @@ -24871,35 +25004,31 @@ package android.media { 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 interface MediaSession2.OnDataSourceMissingHelper { + method public abstract android.media.DataSourceDesc onDataSourceMissing(android.media.MediaSession2, android.media.MediaItem2); } 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); + method public void onBufferingStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2, int); + method public boolean onCommandRequest(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command); + method public android.media.MediaSession2.CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo); + method public void onCurrentMediaItemChanged(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2); + method public void onCustomCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.MediaSession2.Command, android.os.Bundle, android.os.ResultReceiver); + method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo); + method public void onMediaPrepared(android.media.MediaSession2, android.media.MediaPlayerBase, android.media.MediaItem2); + method public void onPlayFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPlayFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPlayFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle); + method public void onPlayerStateChanged(android.media.MediaSession2, android.media.MediaPlayerBase, int); + method public void onPlaylistChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, java.util.List, android.media.MediaMetadata2); + method public void onPlaylistMetadataChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, android.media.MediaMetadata2); + method public void onPrepareFromMediaId(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPrepareFromSearch(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.os.Bundle); + method public void onPrepareFromUri(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.net.Uri, android.os.Bundle); + method public void onRepeatModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int); + method public void onSetRating(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.String, android.media.Rating2); + method public void onShuffleModeChanged(android.media.MediaSession2, android.media.MediaPlaylistAgent, int); } public abstract class MediaSessionService2 extends android.app.Service { @@ -25160,10 +25289,14 @@ package android.media { method public android.media.AudioAttributes getAudioAttributes(); method public deprecated int getStreamType(); method public java.lang.String getTitle(android.content.Context); + method public float getVolume(); + method public boolean isLooping(); method public boolean isPlaying(); method public void play(); method public void setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; + method public void setLooping(boolean); method public deprecated void setStreamType(int); + method public void setVolume(float); method public void stop(); } @@ -25519,6 +25652,7 @@ package android.media.audiofx { field public static final java.util.UUID EFFECT_TYPE_AEC; field public static final java.util.UUID EFFECT_TYPE_AGC; field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST; + field public static final java.util.UUID EFFECT_TYPE_DYNAMICS_PROCESSING; field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB; field public static final java.util.UUID EFFECT_TYPE_EQUALIZER; field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER; @@ -25582,6 +25716,201 @@ package android.media.audiofx { field public short strength; } + public final class DynamicsProcessing extends android.media.audiofx.AudioEffect { + ctor public DynamicsProcessing(int); + ctor public DynamicsProcessing(int, int, android.media.audiofx.DynamicsProcessing.Config); + method public android.media.audiofx.DynamicsProcessing.Channel getChannelByChannelIndex(int); + method public int getChannelCount(); + method public android.media.audiofx.DynamicsProcessing.Config getConfig(); + method public float getInputGainByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.Limiter getLimiterByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.MbcBand getMbcBandByChannelIndex(int, int); + method public android.media.audiofx.DynamicsProcessing.Mbc getMbcByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.EqBand getPostEqBandByChannelIndex(int, int); + method public android.media.audiofx.DynamicsProcessing.Eq getPostEqByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.EqBand getPreEqBandByChannelIndex(int, int); + method public android.media.audiofx.DynamicsProcessing.Eq getPreEqByChannelIndex(int); + method public void setAllChannelsTo(android.media.audiofx.DynamicsProcessing.Channel); + method public void setChannelTo(int, android.media.audiofx.DynamicsProcessing.Channel); + method public void setInputGainAllChannelsTo(float); + method public void setInputGainbyChannel(int, float); + method public void setLimiterAllChannelsTo(android.media.audiofx.DynamicsProcessing.Limiter); + method public void setLimiterByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Limiter); + method public void setMbcAllChannelsTo(android.media.audiofx.DynamicsProcessing.Mbc); + method public void setMbcBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.MbcBand); + method public void setMbcBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.MbcBand); + method public void setMbcByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Mbc); + method public void setPostEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq); + method public void setPostEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPostEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPostEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq); + method public void setPreEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq); + method public void setPreEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPreEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPreEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq); + field public static final int VARIANT_FAVOR_FREQUENCY_RESOLUTION = 0; // 0x0 + field public static final int VARIANT_FAVOR_TIME_RESOLUTION = 1; // 0x1 + } + + public static class DynamicsProcessing.BandBase { + ctor public DynamicsProcessing.BandBase(boolean, float); + method public float getCutoffFrequency(); + method public boolean isEnabled(); + method public void setCutoffFrequency(float); + method public void setEnabled(boolean); + } + + public static class DynamicsProcessing.BandStage extends android.media.audiofx.DynamicsProcessing.Stage { + ctor public DynamicsProcessing.BandStage(boolean, boolean, int); + method public int getBandCount(); + } + + public static final class DynamicsProcessing.Channel { + ctor public DynamicsProcessing.Channel(float, boolean, int, boolean, int, boolean, int, boolean); + ctor public DynamicsProcessing.Channel(android.media.audiofx.DynamicsProcessing.Channel); + method public float getInputGain(); + method public android.media.audiofx.DynamicsProcessing.Limiter getLimiter(); + method public android.media.audiofx.DynamicsProcessing.Mbc getMbc(); + method public android.media.audiofx.DynamicsProcessing.MbcBand getMbcBand(int); + method public android.media.audiofx.DynamicsProcessing.Eq getPostEq(); + method public android.media.audiofx.DynamicsProcessing.EqBand getPostEqBand(int); + method public android.media.audiofx.DynamicsProcessing.Eq getPreEq(); + method public android.media.audiofx.DynamicsProcessing.EqBand getPreEqBand(int); + method public void setInputGain(float); + method public void setLimiter(android.media.audiofx.DynamicsProcessing.Limiter); + method public void setMbc(android.media.audiofx.DynamicsProcessing.Mbc); + method public void setMbcBand(int, android.media.audiofx.DynamicsProcessing.MbcBand); + method public void setPostEq(android.media.audiofx.DynamicsProcessing.Eq); + method public void setPostEqBand(int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPreEq(android.media.audiofx.DynamicsProcessing.Eq); + method public void setPreEqBand(int, android.media.audiofx.DynamicsProcessing.EqBand); + } + + public static final class DynamicsProcessing.Config { + method public android.media.audiofx.DynamicsProcessing.Channel getChannelByChannelIndex(int); + method public float getInputGainByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.Limiter getLimiterByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.MbcBand getMbcBandByChannelIndex(int, int); + method public int getMbcBandCount(); + method public android.media.audiofx.DynamicsProcessing.Mbc getMbcByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.EqBand getPostEqBandByChannelIndex(int, int); + method public int getPostEqBandCount(); + method public android.media.audiofx.DynamicsProcessing.Eq getPostEqByChannelIndex(int); + method public android.media.audiofx.DynamicsProcessing.EqBand getPreEqBandByChannelIndex(int, int); + method public int getPreEqBandCount(); + method public android.media.audiofx.DynamicsProcessing.Eq getPreEqByChannelIndex(int); + method public float getPreferredFrameDuration(); + method public int getVariant(); + method public boolean isLimiterInUse(); + method public boolean isMbcInUse(); + method public boolean isPostEqInUse(); + method public boolean isPreEqInUse(); + method public void setAllChannelsTo(android.media.audiofx.DynamicsProcessing.Channel); + method public void setChannelTo(int, android.media.audiofx.DynamicsProcessing.Channel); + method public void setInputGainAllChannelsTo(float); + method public void setInputGainByChannelIndex(int, float); + method public void setLimiterAllChannelsTo(android.media.audiofx.DynamicsProcessing.Limiter); + method public void setLimiterByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Limiter); + method public void setMbcAllChannelsTo(android.media.audiofx.DynamicsProcessing.Mbc); + method public void setMbcBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.MbcBand); + method public void setMbcBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.MbcBand); + method public void setMbcByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Mbc); + method public void setPostEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq); + method public void setPostEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPostEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPostEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq); + method public void setPreEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq); + method public void setPreEqBandAllChannelsTo(int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPreEqBandByChannelIndex(int, int, android.media.audiofx.DynamicsProcessing.EqBand); + method public void setPreEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq); + } + + public static final class DynamicsProcessing.Config.Builder { + ctor public DynamicsProcessing.Config.Builder(int, int, boolean, int, boolean, int, boolean, int, boolean); + method public android.media.audiofx.DynamicsProcessing.Config build(); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setAllChannelsTo(android.media.audiofx.DynamicsProcessing.Channel); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setChannelTo(int, android.media.audiofx.DynamicsProcessing.Channel); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setInputGainAllChannelsTo(float); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setInputGainByChannelIndex(int, float); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setLimiterAllChannelsTo(android.media.audiofx.DynamicsProcessing.Limiter); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setLimiterByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Limiter); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setMbcAllChannelsTo(android.media.audiofx.DynamicsProcessing.Mbc); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setMbcByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Mbc); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setPostEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setPostEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setPreEqAllChannelsTo(android.media.audiofx.DynamicsProcessing.Eq); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setPreEqByChannelIndex(int, android.media.audiofx.DynamicsProcessing.Eq); + method public android.media.audiofx.DynamicsProcessing.Config.Builder setPreferredFrameDuration(float); + } + + public static final class DynamicsProcessing.Eq extends android.media.audiofx.DynamicsProcessing.BandStage { + ctor public DynamicsProcessing.Eq(boolean, boolean, int); + ctor public DynamicsProcessing.Eq(android.media.audiofx.DynamicsProcessing.Eq); + method public android.media.audiofx.DynamicsProcessing.EqBand getBand(int); + method public void setBand(int, android.media.audiofx.DynamicsProcessing.EqBand); + } + + public static final class DynamicsProcessing.EqBand extends android.media.audiofx.DynamicsProcessing.BandBase { + ctor public DynamicsProcessing.EqBand(boolean, float, float); + ctor public DynamicsProcessing.EqBand(android.media.audiofx.DynamicsProcessing.EqBand); + method public float getGain(); + method public void setGain(float); + } + + public static final class DynamicsProcessing.Limiter extends android.media.audiofx.DynamicsProcessing.Stage { + ctor public DynamicsProcessing.Limiter(boolean, boolean, int, float, float, float, float, float); + ctor public DynamicsProcessing.Limiter(android.media.audiofx.DynamicsProcessing.Limiter); + method public float getAttackTime(); + method public int getLinkGroup(); + method public float getPostGain(); + method public float getRatio(); + method public float getReleaseTime(); + method public float getThreshold(); + method public void setAttackTime(float); + method public void setLinkGroup(int); + method public void setPostGain(float); + method public void setRatio(float); + method public void setReleaseTime(float); + method public void setThreshold(float); + } + + public static final class DynamicsProcessing.Mbc extends android.media.audiofx.DynamicsProcessing.BandStage { + ctor public DynamicsProcessing.Mbc(boolean, boolean, int); + ctor public DynamicsProcessing.Mbc(android.media.audiofx.DynamicsProcessing.Mbc); + method public android.media.audiofx.DynamicsProcessing.MbcBand getBand(int); + method public void setBand(int, android.media.audiofx.DynamicsProcessing.MbcBand); + } + + public static final class DynamicsProcessing.MbcBand extends android.media.audiofx.DynamicsProcessing.BandBase { + ctor public DynamicsProcessing.MbcBand(boolean, float, float, float, float, float, float, float, float, float, float); + ctor public DynamicsProcessing.MbcBand(android.media.audiofx.DynamicsProcessing.MbcBand); + method public float getAttackTime(); + method public float getExpanderRatio(); + method public float getKneeWidth(); + method public float getNoiseGateThreshold(); + method public float getPostGain(); + method public float getPreGain(); + method public float getRatio(); + method public float getReleaseTime(); + method public float getThreshold(); + method public void setAttackTime(float); + method public void setExpanderRatio(float); + method public void setKneeWidth(float); + method public void setNoiseGateThreshold(float); + method public void setPostGain(float); + method public void setPreGain(float); + method public void setRatio(float); + method public void setReleaseTime(float); + method public void setThreshold(float); + } + + public static class DynamicsProcessing.Stage { + ctor public DynamicsProcessing.Stage(boolean, boolean); + method public boolean isEnabled(); + method public boolean isInUse(); + method public void setEnabled(boolean); + } + public class EnvironmentalReverb extends android.media.audiofx.AudioEffect { ctor public EnvironmentalReverb(int, int) throws java.lang.IllegalArgumentException, java.lang.RuntimeException, java.lang.UnsupportedOperationException; method public short getDecayHFRatio() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException; @@ -27260,17 +27589,17 @@ package android.net { field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1 field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3 field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2 - field public static final int TYPE_BLUETOOTH = 7; // 0x7 - field public static final int TYPE_DUMMY = 8; // 0x8 - field public static final int TYPE_ETHERNET = 9; // 0x9 - field public static final int TYPE_MOBILE = 0; // 0x0 - field public static final int TYPE_MOBILE_DUN = 4; // 0x4 + field public static final deprecated int TYPE_BLUETOOTH = 7; // 0x7 + field public static final deprecated int TYPE_DUMMY = 8; // 0x8 + field public static final deprecated int TYPE_ETHERNET = 9; // 0x9 + field public static final deprecated int TYPE_MOBILE = 0; // 0x0 + field public static final deprecated int TYPE_MOBILE_DUN = 4; // 0x4 field public static final deprecated int TYPE_MOBILE_HIPRI = 5; // 0x5 field public static final deprecated int TYPE_MOBILE_MMS = 2; // 0x2 field public static final deprecated int TYPE_MOBILE_SUPL = 3; // 0x3 - field public static final int TYPE_VPN = 17; // 0x11 - field public static final int TYPE_WIFI = 1; // 0x1 - field public static final int TYPE_WIMAX = 6; // 0x6 + field public static final deprecated int TYPE_VPN = 17; // 0x11 + field public static final deprecated int TYPE_WIFI = 1; // 0x1 + field public static final deprecated int TYPE_WIMAX = 6; // 0x6 } public static class ConnectivityManager.NetworkCallback { @@ -27547,16 +27876,16 @@ package android.net { method public int describeContents(); method public android.net.NetworkInfo.DetailedState getDetailedState(); method public java.lang.String getExtraInfo(); - method public java.lang.String getReason(); - method public android.net.NetworkInfo.State getState(); + method public deprecated java.lang.String getReason(); + method public deprecated android.net.NetworkInfo.State getState(); method public int getSubtype(); method public java.lang.String getSubtypeName(); - method public int getType(); - method public java.lang.String getTypeName(); - method public boolean isAvailable(); + method public deprecated int getType(); + method public deprecated java.lang.String getTypeName(); + method public deprecated boolean isAvailable(); method public boolean isConnected(); - method public boolean isConnectedOrConnecting(); - method public boolean isFailover(); + method public deprecated boolean isConnectedOrConnecting(); + method public deprecated boolean isFailover(); method public deprecated boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -33310,6 +33639,7 @@ package android.os { method public static final int getUidForName(java.lang.String); method public static final boolean is64Bit(); method public static boolean isApplicationUid(int); + method public static final boolean isIsolated(); method public static final void killProcess(int); method public static final int myPid(); method public static final int myTid(); @@ -33629,6 +33959,17 @@ package android.os { field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1 field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2 + field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4 + field public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5; // 0x5 + field public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; // 0x2 + field public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; // 0x3 + field public static final int USER_OPERATION_ERROR_MAX_USERS = 6; // 0x6 + field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1 + field public static final int USER_OPERATION_SUCCESS = 0; // 0x0 + } + + public static class UserManager.UserOperationException extends java.lang.RuntimeException { + method public int getUserOperationResult(); } public abstract class VibrationEffect implements android.os.Parcelable { @@ -35103,7 +35444,7 @@ package android.provider { field public static final java.lang.String FEATURES = "features"; 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 = 16; // 0x10 + field public static final int FEATURES_RTT = 32; // 0x20 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"; @@ -36811,6 +37152,7 @@ package android.provider { field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS"; field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS"; field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS"; + field public static final java.lang.String ACTION_DATA_USAGE_SETTINGS = "android.settings.DATA_USAGE_SETTINGS"; field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS"; field public static final java.lang.String ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS"; field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS"; @@ -37244,11 +37586,11 @@ package android.provider { field public static final java.lang.String ADDRESS = "address"; } - public static final class Telephony.CarrierIdentification implements android.provider.BaseColumns { + public static final class Telephony.CarrierId 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 java.lang.String CARRIER_ID = "carrier_id"; + field public static final java.lang.String CARRIER_NAME = "carrier_name"; 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 { @@ -39667,8 +40009,8 @@ package android.service.notification { field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10 field public static final int REASON_USER_STOPPED = 6; // 0x6 field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; - field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 - field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1 + field public static final deprecated int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2 } public static class NotificationListenerService.Ranking { @@ -41046,10 +41388,10 @@ package android.telecom { method public void onStateChanged(android.telecom.Call, int); method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall); field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1 - field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3 - field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2 - field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4 - field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5; // 0x5 + field public static final int HANDOVER_FAILURE_NOT_SUPPORTED = 2; // 0x2 + field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4; // 0x4 + field public static final int HANDOVER_FAILURE_UNKNOWN = 5; // 0x5 + field public static final int HANDOVER_FAILURE_USER_REJECTED = 3; // 0x3 } public static class Call.Details { @@ -41806,11 +42148,9 @@ package android.telecom { package android.telephony { public final class AccessNetworkConstants { - ctor public AccessNetworkConstants(); } public static final class AccessNetworkConstants.AccessNetworkType { - ctor public AccessNetworkConstants.AccessNetworkType(); field public static final int CDMA2000 = 4; // 0x4 field public static final int EUTRAN = 3; // 0x3 field public static final int GERAN = 1; // 0x1 @@ -41820,7 +42160,6 @@ package android.telephony { } public static final class AccessNetworkConstants.EutranBand { - ctor public AccessNetworkConstants.EutranBand(); field public static final int BAND_1 = 1; // 0x1 field public static final int BAND_10 = 10; // 0xa field public static final int BAND_11 = 11; // 0xb @@ -41872,7 +42211,6 @@ package android.telephony { } public static final class AccessNetworkConstants.GeranBand { - ctor public AccessNetworkConstants.GeranBand(); field public static final int BAND_450 = 3; // 0x3 field public static final int BAND_480 = 4; // 0x4 field public static final int BAND_710 = 5; // 0x5 @@ -41890,7 +42228,6 @@ package android.telephony { } public static final class AccessNetworkConstants.UtranBand { - ctor public AccessNetworkConstants.UtranBand(); field public static final int BAND_1 = 1; // 0x1 field public static final int BAND_10 = 10; // 0xa field public static final int BAND_11 = 11; // 0xb @@ -41943,6 +42280,8 @@ package android.telephony { field public static final java.lang.String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING = "carrier_instant_lettering_escaped_chars_string"; field public static final java.lang.String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING = "carrier_instant_lettering_invalid_chars_string"; field public static final java.lang.String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int"; + field public static final java.lang.String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool"; + field public static final java.lang.String KEY_CARRIER_NAME_STRING = "carrier_name_string"; field public static final java.lang.String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; field public static final java.lang.String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool"; field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool"; @@ -42091,9 +42430,9 @@ package android.telephony { method public int getCid(); method public int getLac(); method public deprecated int getMcc(); - method public java.lang.String getMccStr(); + method public java.lang.String getMccString(); method public deprecated int getMnc(); - method public java.lang.String getMncStr(); + method public java.lang.String getMncString(); method public java.lang.String getMobileNetworkOperator(); method public java.lang.CharSequence getOperatorAlphaLong(); method public java.lang.CharSequence getOperatorAlphaShort(); @@ -42107,9 +42446,9 @@ package android.telephony { method public int getCi(); method public int getEarfcn(); method public deprecated int getMcc(); - method public java.lang.String getMccStr(); + method public java.lang.String getMccString(); method public deprecated int getMnc(); - method public java.lang.String getMncStr(); + method public java.lang.String getMncString(); method public java.lang.String getMobileNetworkOperator(); method public java.lang.CharSequence getOperatorAlphaLong(); method public java.lang.CharSequence getOperatorAlphaShort(); @@ -42123,8 +42462,8 @@ package android.telephony { method public int getCid(); method public int getCpid(); method public int getLac(); - method public java.lang.String getMccStr(); - method public java.lang.String getMncStr(); + method public java.lang.String getMccString(); + method public java.lang.String getMncString(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -42133,9 +42472,9 @@ package android.telephony { method public int getCid(); method public int getLac(); method public deprecated int getMcc(); - method public java.lang.String getMccStr(); + method public java.lang.String getMccString(); method public deprecated int getMnc(); - method public java.lang.String getMncStr(); + method public java.lang.String getMncString(); method public java.lang.String getMobileNetworkOperator(); method public java.lang.CharSequence getOperatorAlphaLong(); method public java.lang.CharSequence getOperatorAlphaShort(); @@ -42329,7 +42668,8 @@ package android.telephony { } public class NetworkScan { - method public void stop() throws android.os.RemoteException; + method public deprecated void stop() throws android.os.RemoteException; + method public void stopScan(); field public static final int ERROR_INTERRUPTED = 10002; // 0x2712 field public static final int ERROR_INVALID_SCAN = 2; // 0x2 field public static final int ERROR_INVALID_SCANID = 10001; // 0x2711 @@ -42794,7 +43134,8 @@ package android.telephony { method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); method public void listen(android.telephony.PhoneStateListener, int); - method public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); + method public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); + method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(java.lang.String); method public java.lang.String sendEnvelopeWithStatus(java.lang.String); method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); @@ -46282,6 +46623,7 @@ package android.view { } public final class DisplayCutout { + ctor public DisplayCutout(android.graphics.Rect, android.graphics.Region); method public android.graphics.Region getBounds(); method public int getSafeInsetBottom(); method public int getSafeInsetLeft(); @@ -48533,6 +48875,7 @@ package android.view { method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); method public boolean hasPermanentMenuKey(); + method public boolean shouldShowMenuShortcutsWhenKeyboardPresent(); } public class ViewDebug { @@ -50487,7 +50830,6 @@ package android.view.inputmethod { method public abstract boolean performEditorAction(int); method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle); method public abstract boolean reportFullscreenMode(boolean); - method public default void reportLanguageHint(android.os.LocaleList); method public abstract boolean requestCursorUpdates(int); method public abstract boolean sendKeyEvent(android.view.KeyEvent); method public abstract boolean setComposingRegion(int, int); @@ -53054,10 +53396,27 @@ package android.widget { public final class Magnifier { ctor public Magnifier(android.view.View); method public void dismiss(); + method public int getHeight(); + method public int getWidth(); + method public float getZoom(); method public void show(float, float); method public void update(); } + public class MediaControlView2 extends android.view.ViewGroup { + ctor public MediaControlView2(android.content.Context); + ctor public MediaControlView2(android.content.Context, android.util.AttributeSet); + ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int); + ctor public MediaControlView2(android.content.Context, android.util.AttributeSet, int, int); + method public void requestPlayButtonFocus(); + method public void setMediaSessionToken(android.media.SessionToken2); + method public void setOnFullScreenListener(android.widget.MediaControlView2.OnFullScreenListener); + } + + public static abstract interface MediaControlView2.OnFullScreenListener { + method public abstract void onFullScreen(android.view.View, boolean); + } + public class MediaController extends android.widget.FrameLayout { ctor public MediaController(android.content.Context, android.util.AttributeSet); ctor public MediaController(android.content.Context, boolean); @@ -54448,6 +54807,27 @@ package android.widget { method public void suspend(); } + public class VideoView2 extends android.view.ViewGroup { + ctor public VideoView2(android.content.Context); + ctor public VideoView2(android.content.Context, android.util.AttributeSet); + ctor public VideoView2(android.content.Context, android.util.AttributeSet, int); + ctor public VideoView2(android.content.Context, android.util.AttributeSet, int, int); + method public android.widget.MediaControlView2 getMediaControlView2(); + method public android.media.SessionToken2 getMediaSessionToken(); + method public int getViewType(); + method public boolean isSubtitleEnabled(); + method public void setAudioAttributes(android.media.AudioAttributes); + method public void setAudioFocusRequest(int); + method public void setDataSource(android.media.DataSourceDesc); + method public void setMediaControlView2(android.widget.MediaControlView2, long); + method public void setMediaItem(android.media.MediaItem2); + method public void setSpeed(float); + method public void setSubtitleEnabled(boolean); + method public void setViewType(int); + field public static final int VIEW_TYPE_SURFACEVIEW = 1; // 0x1 + field public static final int VIEW_TYPE_TEXTUREVIEW = 2; // 0x2 + } + public class ViewAnimator extends android.widget.FrameLayout { ctor public ViewAnimator(android.content.Context); ctor public ViewAnimator(android.content.Context, android.util.AttributeSet); diff --git a/api/removed.txt b/api/removed.txt index 79c54fde547745940c9ebcf6c766220febf04dc7..a5370f48fb3ad2cd7d7001a1c6749dc129520ce1 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -152,6 +152,11 @@ package android.graphics { method public deprecated boolean clipRegion(android.graphics.Region); } + public final class ImageDecoder implements java.lang.AutoCloseable { + method public deprecated boolean getAsAlphaMask(); + method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean); + } + public deprecated class LayerRasterizer extends android.graphics.Rasterizer { ctor public LayerRasterizer(); method public void addLayer(android.graphics.Paint, float, float); @@ -173,6 +178,16 @@ package android.graphics { } +package android.graphics.drawable { + + public class AnimatedImageDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable2 { + method public deprecated int getLoopCount(int); + method public deprecated void setLoopCount(int); + field public static final deprecated int LOOP_INFINITE = -1; // 0xffffffff + } + +} + package android.hardware { public final class HardwareBuffer implements java.lang.AutoCloseable android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 3aca59a5724d7f245d7c04f2a25c5d8668f49c11..d35a06be45dfa5bdc7038927585a27e32900bf14 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1,6 +1,7 @@ package android { public static final class Manifest.permission { + field public static final java.lang.String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS"; field public static final java.lang.String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO"; field public static final java.lang.String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM"; field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES"; @@ -617,6 +618,7 @@ package android.app.backup { method public java.lang.String transportDirName(); field public static final int AGENT_ERROR = -1003; // 0xfffffc15 field public static final int AGENT_UNKNOWN = -1004; // 0xfffffc14 + field public static final java.lang.String EXTRA_TRANSPORT_REGISTRATION = "android.app.backup.extra.TRANSPORT_REGISTRATION"; field public static final int FLAG_INCREMENTAL = 2; // 0x2 field public static final int FLAG_NON_INCREMENTAL = 4; // 0x4 field public static final int FLAG_USER_INITIATED = 1; // 0x1 @@ -838,6 +840,7 @@ package android.content { } public class Intent implements java.lang.Cloneable android.os.Parcelable { + field public static final java.lang.String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED"; field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY"; field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET"; @@ -1219,6 +1222,7 @@ package android.hardware.display { } public final class DisplayManager { + method public java.util.List getAmbientBrightnessStats(); method public java.util.List getBrightnessEvents(); method public android.graphics.Point getStableDisplaySize(); method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); @@ -1591,7 +1595,7 @@ package android.hardware.location { method public abstract void onMessageReceipt(int, int, android.hardware.location.ContextHubMessage); } - public deprecated class ContextHubMessage { + public deprecated class ContextHubMessage implements android.os.Parcelable { ctor public ContextHubMessage(int, int, byte[]); method public int describeContents(); method public byte[] getData(); @@ -1722,7 +1726,7 @@ package android.hardware.location { field public static final android.os.Parcelable.Creator CREATOR; } - public deprecated class NanoApp { + public deprecated class NanoApp implements android.os.Parcelable { ctor public NanoApp(); ctor public deprecated NanoApp(int, byte[]); ctor public NanoApp(long, byte[]); @@ -1770,7 +1774,7 @@ package android.hardware.location { field public static final android.os.Parcelable.Creator CREATOR; } - public deprecated class NanoAppFilter { + public deprecated class NanoAppFilter implements android.os.Parcelable { ctor public NanoAppFilter(long, int, int, long); method public int describeContents(); method public boolean testMatch(android.hardware.location.NanoAppInstanceInfo); @@ -1785,7 +1789,7 @@ package android.hardware.location { field public static final int VENDOR_ANY = -1; // 0xffffffff } - public deprecated class NanoAppInstanceInfo { + public deprecated class NanoAppInstanceInfo implements android.os.Parcelable { ctor public NanoAppInstanceInfo(); method public int describeContents(); method public long getAppId(); @@ -2114,8 +2118,15 @@ package android.hardware.radio { field public static final java.lang.String METADATA_KEY_ART = "android.hardware.radio.metadata.ART"; field public static final java.lang.String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST"; field public static final java.lang.String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK"; + field public static final java.lang.String METADATA_KEY_DAB_COMPONENT_NAME = "android.hardware.radio.metadata.DAB_COMPONENT_NAME"; + field public static final java.lang.String METADATA_KEY_DAB_COMPONENT_NAME_SHORT = "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT"; + field public static final java.lang.String METADATA_KEY_DAB_ENSEMBLE_NAME = "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME"; + field public static final java.lang.String METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT = "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME_SHORT"; + field public static final java.lang.String METADATA_KEY_DAB_SERVICE_NAME = "android.hardware.radio.metadata.DAB_SERVICE_NAME"; + field public static final java.lang.String METADATA_KEY_DAB_SERVICE_NAME_SHORT = "android.hardware.radio.metadata.DAB_SERVICE_NAME_SHORT"; field public static final java.lang.String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE"; field public static final java.lang.String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON"; + field public static final java.lang.String METADATA_KEY_PROGRAM_NAME = "android.hardware.radio.metadata.PROGRAM_NAME"; field public static final java.lang.String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY"; field public static final java.lang.String METADATA_KEY_RDS_PI = "android.hardware.radio.metadata.RDS_PI"; field public static final java.lang.String METADATA_KEY_RDS_PS = "android.hardware.radio.metadata.RDS_PS"; @@ -2648,8 +2659,10 @@ package android.media.audiopolicy { } public class AudioPolicy { + method public int attachMixes(java.util.List); method public android.media.AudioRecord createAudioRecordSink(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; + method public int detachMixes(java.util.List); method public int getFocusDuckingBehavior(); method public int getStatus(); method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; @@ -3600,6 +3613,11 @@ package android.nfc { package android.os { + public class BatteryManager { + field public static final java.lang.String EXTRA_EVENTS = "android.os.extra.EVENTS"; + field public static final java.lang.String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP"; + } + public final class ConfigUpdate { field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS"; field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS"; @@ -4180,6 +4198,7 @@ package android.provider { 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 EUICC_PROVISIONED = "euicc_provisioned"; 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"; @@ -4275,6 +4294,7 @@ package android.security.keystore.recovery { method public byte[] getServerParams(); method public int getSnapshotVersion(); method public java.security.cert.CertPath getTrustedHardwareCertPath(); + method public deprecated byte[] getTrustedHardwarePublicKey(); method public java.util.List getWrappedApplicationKeys(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -4297,16 +4317,22 @@ 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 deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException; method public java.security.Key generateKey(java.lang.String) 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 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 deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException; method public int[] getRecoverySecretTypes() 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 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 deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException; + method public void initRecoveryService(java.lang.String, byte[], 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 deprecated 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; @@ -4318,6 +4344,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 deprecated 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; } @@ -4327,6 +4354,7 @@ package android.security.keystore.recovery { public final class WrappedApplicationKey implements android.os.Parcelable { method public int describeContents(); + method public deprecated byte[] getAccount(); method public java.lang.String getAlias(); method public byte[] getEncryptedKeyMaterial(); method public void writeToParcel(android.os.Parcel, int); @@ -4336,6 +4364,7 @@ package android.security.keystore.recovery { public static class WrappedApplicationKey.Builder { ctor public WrappedApplicationKey.Builder(); method public android.security.keystore.recovery.WrappedApplicationKey build(); + method public deprecated 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[]); } @@ -4972,7 +5001,6 @@ package android.telecom { package android.telephony { public static final class AccessNetworkConstants.TransportType { - ctor public AccessNetworkConstants.TransportType(); field public static final int WLAN = 2; // 0x2 field public static final int WWAN = 1; // 0x1 } @@ -5384,6 +5412,7 @@ package android.telephony.euicc { 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_EUICC_NOT_FOUND = -2; // 0xfffffffe field public static final int RESULT_OK = 0; // 0x0 field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff } @@ -5983,7 +6012,6 @@ package android.telephony.ims.feature { method public void setUiTtyMode(int, android.os.Message); method public int shouldProcessCall(java.lang.String[]); field public static final int PROCESS_CALL_CSFB = 1; // 0x1 - field public static final int PROCESS_CALL_EMERGENCY_CSFB = 2; // 0x2 field public static final int PROCESS_CALL_IMS = 0; // 0x0 } @@ -6641,6 +6669,7 @@ package android.webkit { method public abstract android.os.Handler getHandler(android.os.Handler); method public abstract void onActivityResult(int, int, android.content.Intent); method public abstract void onAttachedToWindow(); + method public default boolean onCheckIsTextEditor(); method public abstract void onConfigurationChanged(android.content.res.Configuration); method public abstract android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo); method public abstract void onDetachedFromWindow(); diff --git a/api/system-removed.txt b/api/system-removed.txt index 58652a297bd8c72f0d27b1d36ca16264ca0ef368..48f43e0880daea0e55f600bbdf16726967b50e5d 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -91,34 +91,6 @@ 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 d5b43115c1259f85e7589ecac90cc34082e3a7f2..9d67f4c3bb602607dd4b2943a72eb5d759f67ce1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -12,6 +12,7 @@ package android.app { public class ActivityManager { method public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); method public int getPackageImportance(java.lang.String); + method public long getTotalRam(); method public int getUidImportance(int); method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException; @@ -47,7 +48,10 @@ package android.app { public class AppOpsManager { method public static java.lang.String[] getOpStrs(); + method public boolean isOperationActive(int, int, java.lang.String); method public void setMode(int, int, java.lang.String, int); + method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener); + method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener); field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; @@ -89,6 +93,12 @@ package android.app { field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms"; field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms"; field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper"; + field public static final int OP_RECORD_AUDIO = 27; // 0x1b + field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18 + } + + public static abstract interface AppOpsManager.OnOpActiveChangedListener { + method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean); } public final class NotificationChannelGroup implements android.os.Parcelable { @@ -350,7 +360,9 @@ package android.hardware.display { } public final class DisplayManager { + method public java.util.List getAmbientBrightnessStats(); method public java.util.List getBrightnessEvents(); + method public android.graphics.Point getStableDisplaySize(); method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); } @@ -446,6 +458,10 @@ package android.net { package android.os { + public static class Build.VERSION { + field public static final int RESOURCES_SDK_INT; + } + public class IncidentManager { method public void reportIncident(android.os.IncidentReportArgs); method public void reportIncident(java.lang.String, byte[]); @@ -546,6 +562,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 HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; 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"; diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index e87a78e20d1442bd0c292b791d2343408fd6dbd5..84a04e5ad6e34913685174d26fc597ab094bd7eb 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -297,6 +297,10 @@ public final class Bmgr { super.backupFinished(status); System.out.println("Backup finished with result: " + convertBackupStatusToString(status)); + if (status == BackupManager.ERROR_BACKUP_CANCELLED) { + System.out.println("Backups can be cancelled if a backup is already running, check " + + "backup dumpsys"); + } } } @@ -318,7 +322,7 @@ public final class Bmgr { case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED: return "Size quota exceeded"; case BackupManager.ERROR_BACKUP_CANCELLED: - return "Backup Cancelled"; + return "Backup cancelled"; default: return "Unknown error"; } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 54785ca131859d1e3c4e26221e0af502a601554f..8ffe5bf59315743551862b4200eaa6051b9ba476 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -29,11 +29,11 @@ #include #include +#include #include #include #include -#include #include #include #include diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 847b26a39ffe3a342668997e0c2ec166c2693ff2..5b413e99cc3b9037cb106e91c94bc6fc173a8236 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -440,7 +440,7 @@ void Message::startSession(ProtoOutputStream* proto, const string& name) { uint64_t fieldId = mTable->mFields[name]; - long long token = proto->start(fieldId); + uint64_t token = proto->start(fieldId); mPreviousField = name; mTokens.push(token); } diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index 53f443873e4da1f27d1f7ba5c88b31cbe59baef0..c4eda4af43333d661377e1d27feac2ca5ebb7e71 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -194,7 +194,7 @@ public: private: Table* mTable; std::string mPreviousField; - stack mTokens; + stack mTokens; map mSubMessages; }; diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp index fde17bd97a7f72a475f8fa142fc1cdee064ad554..43a12f603ba38dc75cf3bb2ba6ecb2bac7725220 100644 --- a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp @@ -65,10 +65,10 @@ CpuFreqParser::Parse(const int in, const int out) const 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(CpuFreqProto::Stats::TIMES); + uint64_t 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); diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp index b2b431c628661a43939ee3d885099563772a3ca9..eed68b9e29c6365a9f8b020df49f48dc3d9e50ef 100644 --- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp @@ -28,7 +28,7 @@ static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId, const int count, const char* names[], const uint64_t ids[]) { record_t record = parseRecord(line, delimiter); - long long token = proto->start(fieldId); + uint64_t token = proto->start(fieldId); for (int i=0; i<(int)record.size(); i++) { for (int j=0; j0; 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(ProcrankProto::Summary::ZRAM); + uint64_t 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(ProcrankProto::Summary::RAM); + uint64_t token = proto.start(ProcrankProto::Summary::RAM); proto.write(ProcrankProto::Summary::Ram::RAW_TEXT, ram); proto.end(token); } diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp index 420775fb5f04b66067d656504afaaf1c520adbbb..8d64064e26f4e8380e9e0303f04d5b1110ef3c63 100644 --- a/cmds/incident_helper/src/parsers/PsParser.cpp +++ b/cmds/incident_helper/src/parsers/PsParser.cpp @@ -71,7 +71,7 @@ status_t PsParser::Parse(const int in, const int out) const { continue; } - long long token = proto.start(PsProto::PROCESSES); + uint64_t 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/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp index 7b0ac0b8452bec7bf1e2e84334e8f686610bd8ec..eba536bd9e9cc10a01069c983f192930bd584c53 100644 --- a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp +++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp @@ -207,7 +207,7 @@ SystemPropertiesParser::Parse(const int in, const int out) const sysProp.endSession(&proto); for (auto it = extras.begin(); it != extras.end(); it++) { - long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES); + uint64_t token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES); proto.write(SystemPropertiesProto::Property::NAME, it->first); proto.write(SystemPropertiesProto::Property::VALUE, it->second); proto.end(token); diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index 6bdd9becff37b3a33c4a475dd01ec59ff626d971..db864aed6bdc32528bf376e39c9b6c1d105c5aa4 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -15,7 +15,10 @@ LOCAL_PATH:= $(call my-dir) # proto files used in incidentd to generate cppstream proto headers. -PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto +PROTO_FILES:= \ + frameworks/base/core/proto/android/os/backtrace.proto \ + frameworks/base/core/proto/android/os/data.proto \ + frameworks/base/core/proto/android/util/log.proto # ========= # # incidentd # @@ -38,18 +41,16 @@ else LOCAL_CFLAGS += \ -Os endif - LOCAL_C_INCLUDES += $(LOCAL_PATH)/src LOCAL_SHARED_LIBRARIES := \ libbase \ libbinder \ - libcutils \ + libdebuggerd_client \ + libdumputils \ libincident \ liblog \ - libprotobuf-cpp-lite \ libprotoutil \ - libselinux \ libservices \ libutils @@ -117,12 +118,12 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_SHARED_LIBRARIES := \ libbase \ libbinder \ - libcutils \ + libdebuggerd_client \ + libdumputils \ libincident \ liblog \ libprotobuf-cpp-lite \ libprotoutil \ - libselinux \ libservices \ libutils \ @@ -131,7 +132,7 @@ LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata) LOCAL_MODULE_CLASS := NATIVE_TESTS gen_src_dir := $(local-generated-sources-dir) # generate cppstream proto for testing -GEN_PROTO := $(gen_src_dir)/log.proto.timestamp +GEN_PROTO := $(gen_src_dir)/test.proto.timestamp $(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES) $(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir) $(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \ diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc index 1bd146850ea9b7e82638edfbee2864b119b5220e..9c16a1c52e8923a2be1fcd143fbef21e746c1432 100644 --- a/cmds/incidentd/incidentd.rc +++ b/cmds/incidentd/incidentd.rc @@ -15,7 +15,8 @@ service incidentd /system/bin/incidentd class main user incidentd - group incidentd log + group incidentd log readproc + capabilities KILL SYS_PTRACE on post-fs-data # Create directory for incidentd diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index db60794a72255dfd5cd2737060d00d073d094bcd..35701446e9d94e87564809e1cf2492a1a6bbb801 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -76,6 +76,7 @@ status_t FdBuffer::read(int fd, int64_t timeout) { return -errno; } } else if (amt == 0) { + VLOG("Reached EOF of fd=%d", fd); break; } mBuffer.wp()->move(amt); @@ -86,6 +87,35 @@ status_t FdBuffer::read(int fd, int64_t timeout) { return NO_ERROR; } +status_t FdBuffer::readFully(int fd) { + mStartTime = uptimeMillis(); + + while (true) { + if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { + // Don't let it get too big. + mTruncated = true; + VLOG("Truncating data"); + break; + } + if (mBuffer.writeBuffer() == NULL) return NO_MEMORY; + + ssize_t amt = + TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite())); + if (amt < 0) { + VLOG("Fail to read %d: %s", fd, strerror(errno)); + return -errno; + } else if (amt == 0) { + VLOG("Done reading %zu bytes", mBuffer.size()); + // We're done. + break; + } + mBuffer.wp()->move(amt); + } + + mFinishTime = uptimeMillis(); + return NO_ERROR; +} + status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs) { struct pollfd pfds[] = { @@ -156,10 +186,10 @@ status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { VLOG("Fail to read fd %d: %s", fd, strerror(errno)); return -errno; - } // 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; + } // otherwise just continue + } else if (amt == 0) { + VLOG("Reached EOF of input file %d", fd); + pfds[0].fd = -1; // reach EOF so don't have to poll pfds[0]. } else { rpos += amt; cirSize += amt; @@ -187,6 +217,7 @@ status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64 // if buffer is empty and fd is closed, close write fd. if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) { + VLOG("Close write pipe %d", toFd); ::close(pfds[1].fd); pfds[1].fd = -1; } @@ -207,6 +238,7 @@ status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64 return -errno; } // otherwise just continue } else if (amt == 0) { + VLOG("Reached EOF of fromFd %d", fromFd); break; } else { mBuffer.wp()->move(amt); diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h index 5bfa0938f5e88fa5ba3e86f06389e62716b77480..34ebcf50905dee616b90b903c6fd70850f885585 100644 --- a/cmds/incidentd/src/FdBuffer.h +++ b/cmds/incidentd/src/FdBuffer.h @@ -26,7 +26,7 @@ using namespace android::util; using namespace std; /** - * Reads a file into a buffer, and then writes that data to an FdSet. + * Reads data from fd into a buffer, fd must be closed explicitly. */ class FdBuffer { public: @@ -40,6 +40,12 @@ public: */ status_t read(int fd, int64_t timeoutMs); + /** + * Read the data until we hit eof. + * Returns NO_ERROR if there were no errors. + */ + status_t readFully(int fd); + /** * Read processed results by streaming data to a parsing process, e.g. incident helper. * The parsing process provides IO fds which are 'toFd' and 'fromFd'. The function @@ -83,6 +89,11 @@ public: */ EncodedBuffer::iterator data() const; + /** + * Return the internal buffer, don't call unless you are familiar with EncodedBuffer. + */ + EncodedBuffer* getInternalBuffer() { return &mBuffer; } + private: EncodedBuffer mBuffer; int64_t mStartTime; diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 28fb38a79a3582683d458b5f84dae5b1fc6d3709..d02b4dd99067ea69b41553b6cac483dd821c8026 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -358,8 +358,7 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector 3 ? atoi(args[3]) : -1); error = pBuf.strip(spec); diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp index ee57f4d0e5d6f154c14802d42b3812ce086ef9a3..f1f7c589cf431d29882c3e1aba74c10ccd15d203 100644 --- a/cmds/incidentd/src/PrivacyBuffer.cpp +++ b/cmds/incidentd/src/PrivacyBuffer.cpp @@ -91,7 +91,7 @@ status_t PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpe // current field is message type and its sub-fields have extra privacy policies uint32_t msgSize = mData.readRawVarint(); size_t start = mData.rp()->pos(); - long long token = mProto.start(encode_field_id(policy)); + uint64_t token = mProto.start(encode_field_id(policy)); while (mData.rp()->pos() - start != msgSize) { status_t err = stripField(policy, spec, depth + 1); if (err != NO_ERROR) return err; @@ -101,7 +101,7 @@ status_t PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpe } // ================================================================================ -PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data) +PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator data) : mPolicy(policy), mData(data), mProto(), mSize(0) {} PrivacyBuffer::~PrivacyBuffer() {} diff --git a/cmds/incidentd/src/PrivacyBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h index 92e1a2572465148287f19a509ae73bc4ffe738ce..cd29d8b68591fb002529eba85bf5992ab960425d 100644 --- a/cmds/incidentd/src/PrivacyBuffer.h +++ b/cmds/incidentd/src/PrivacyBuffer.h @@ -34,7 +34,7 @@ using namespace android::util; */ class PrivacyBuffer { public: - PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data); + PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator data); ~PrivacyBuffer(); /** @@ -60,7 +60,7 @@ public: private: const Privacy* mPolicy; - EncodedBuffer::iterator& mData; + EncodedBuffer::iterator mData; ProtoOutputStream mProto; size_t mSize; diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index 12764f8ff09c9a09f44745be6d5031bd724c8503..fc8a6db59e4bb60d5646c9e13cfd80a86730b28d 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -89,11 +89,11 @@ bool ReportRequestSet::containsSection(int id) { return mSections.containsSectio IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) { if (mSectionStats.find(id) == mSectionStats.end()) { - auto stats = mMetadata.add_sections(); - stats->set_id(id); + IncidentMetadata::SectionStats stats; + stats.set_id(id); mSectionStats[id] = stats; } - return mSectionStats[id]; + return &mSectionStats[id]; } // ================================================================================ @@ -182,7 +182,7 @@ Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) { } // Execute - go get the data and write it into the file descriptors. - auto stats = batch.sectionStats(id); + IncidentMetadata::SectionStats* stats = batch.sectionStats(id); int64_t startTime = uptimeMillis(); err = (*section)->Execute(&batch); int64_t endTime = uptimeMillis(); diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index ba8965ef4a6cc397a507393b6d12d88367edcb7f..f9a092a0ca7892c5a5a138e1ef693464aebcce44 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -66,6 +66,7 @@ public: int mainFd() { return mMainFd; } int mainDest() { return mMainDest; } IncidentMetadata& metadata() { return mMetadata; } + map& allSectionStats() { return mSectionStats; } bool containsSection(int id); IncidentMetadata::SectionStats* sectionStats(int id); @@ -77,7 +78,7 @@ private: int mMainDest; IncidentMetadata mMetadata; - map mSectionStats; + map mSectionStats; }; // ================================================================================ diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 64eae3acd34c1ae69827377c127c2ae45b7f890e..5cde5a94e2dd69ad164c67186b49a09379e3aa2b 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -18,17 +18,19 @@ #include "Section.h" +#include #include -#include -#include #include -#include #include +#include #include +#include #include #include +#include +#include #include #include #include @@ -37,6 +39,8 @@ #include "FdBuffer.h" #include "Privacy.h" #include "PrivacyBuffer.h" +#include "frameworks/base/core/proto/android/os/backtrace.proto.h" +#include "frameworks/base/core/proto/android/os/data.proto.h" #include "frameworks/base/core/proto/android/util/log.proto.h" #include "incidentd_util.h" @@ -52,31 +56,11 @@ const int FIELD_ID_INCIDENT_METADATA = 2; const int WAIT_MAX = 5; const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000}; const char INCIDENT_HELPER[] = "/system/bin/incident_helper"; +const char GZIP[] = "/system/bin/gzip"; -static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, - Fpipe& c2pPipe) { +static pid_t fork_execute_incident_helper(const int id, 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()) { - ALOGW("%s can't setup stdin and stdout for incident helper", name); - _exit(EXIT_FAILURE); - } - - /* make sure the child dies when incidentd dies */ - prctl(PR_SET_PDEATHSIG, SIGKILL); - - 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 - } - // close the fds used in incident helper - close(p2cPipe.readFd()); - close(c2pPipe.writeFd()); - return pid; + return fork_execute_cmd(INCIDENT_HELPER, const_cast(ihArgs), p2cPipe, c2pPipe); } // ================================================================================ @@ -118,13 +102,14 @@ static status_t write_section_header(int fd, int sectionId, size_t size) { return WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno; } +// Reads data from FdBuffer and writes it to the requests file descriptor. 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); + IncidentMetadata::SectionStats* stats = requests->sectionStats(id); stats->set_dump_size_bytes(data.size()); stats->set_dump_duration_ms(buffer.durationMs()); @@ -230,23 +215,48 @@ MetadataSection::MetadataSection() : Section(FIELD_ID_INCIDENT_METADATA, 0) {} MetadataSection::~MetadataSection() {} status_t MetadataSection::Execute(ReportRequestSet* requests) const { - std::string metadataBuf; - requests->metadata().SerializeToString(&metadataBuf); + ProtoOutputStream proto; + IncidentMetadata metadata = requests->metadata(); + proto.write(FIELD_TYPE_ENUM | IncidentMetadata::kDestFieldNumber, metadata.dest()); + proto.write(FIELD_TYPE_INT32 | IncidentMetadata::kRequestSizeFieldNumber, + metadata.request_size()); + proto.write(FIELD_TYPE_BOOL | IncidentMetadata::kUseDropboxFieldNumber, metadata.use_dropbox()); + for (auto iter = requests->allSectionStats().begin(); iter != requests->allSectionStats().end(); + iter++) { + IncidentMetadata::SectionStats stats = iter->second; + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | IncidentMetadata::kSectionsFieldNumber); + proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kIdFieldNumber, stats.id()); + proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kSuccessFieldNumber, + stats.success()); + proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kReportSizeBytesFieldNumber, + stats.report_size_bytes()); + proto.write(FIELD_TYPE_INT64 | IncidentMetadata::SectionStats::kExecDurationMsFieldNumber, + stats.exec_duration_ms()); + proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kDumpSizeBytesFieldNumber, + stats.dump_size_bytes()); + proto.write(FIELD_TYPE_INT64 | IncidentMetadata::SectionStats::kDumpDurationMsFieldNumber, + stats.dump_duration_ms()); + proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kTimedOutFieldNumber, + stats.timed_out()); + proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kIsTruncatedFieldNumber, + stats.is_truncated()); + proto.end(token); + } + for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { const sp request = *it; - if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) { + if (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())) { + write_section_header(request->fd, id, proto.size()); + if (!proto.flush(request->fd)) { 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())) { + if (requests->mainFd() >= 0) { + write_section_header(requests->mainFd(), id, proto.size()); + if (!proto.flush(requests->mainFd())) { ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd()); return -1; } @@ -254,10 +264,12 @@ status_t MetadataSection::Execute(ReportRequestSet* requests) const { return NO_ERROR; } // ================================================================================ +static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sys/", 5) == 0; } + FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) : Section(id, timeoutMs), mFilename(filename) { name = filename; - mIsSysfs = strncmp(filename, "/sys/", 5) == 0; + mIsSysfs = isSysfs(filename); } FileSection::~FileSection() {} @@ -280,7 +292,7 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { return -errno; } - pid_t pid = fork_execute_incident_helper(this->id, this->name.string(), p2cPipe, c2pPipe); + pid_t pid = fork_execute_incident_helper(this->id, &p2cPipe, &c2pPipe); if (pid == -1) { ALOGW("FileSection '%s' failed to fork", this->name.string()); return -errno; @@ -289,6 +301,8 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { // parent process status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(), this->timeoutMs, mIsSysfs); + close(fd); // close the fd anyway. + 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"); @@ -313,6 +327,99 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { return NO_ERROR; } +// ================================================================================ +GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) { + name = "gzip "; + name += filename; + va_list args; + va_start(args, filename); + mFilenames = varargs(filename, args); + va_end(args); +} + +GZipSection::~GZipSection() {} + +status_t GZipSection::Execute(ReportRequestSet* requests) const { + // Reads the files in order, use the first available one. + int index = 0; + int fd = -1; + while (mFilenames[index] != NULL) { + fd = open(mFilenames[index], O_RDONLY | O_CLOEXEC); + if (fd != -1) { + break; + } + ALOGW("GZipSection failed to open file %s", mFilenames[index]); + index++; // look at the next file. + } + VLOG("GZipSection is using file %s, fd=%d", mFilenames[index], fd); + if (fd == -1) return -1; + + FdBuffer buffer; + Fpipe p2cPipe; + Fpipe c2pPipe; + // initiate pipes to pass data to/from gzip + if (!p2cPipe.init() || !c2pPipe.init()) { + ALOGW("GZipSection '%s' failed to setup pipes", this->name.string()); + return -errno; + } + + const char* gzipArgs[]{GZIP, NULL}; + pid_t pid = fork_execute_cmd(GZIP, const_cast(gzipArgs), &p2cPipe, &c2pPipe); + if (pid == -1) { + ALOGW("GZipSection '%s' failed to fork", this->name.string()); + return -errno; + } + // parent process + + // construct Fdbuffer to output GZippedfileProto, the reason to do this instead of using + // ProtoOutputStream is to avoid allocation of another buffer inside ProtoOutputStream. + EncodedBuffer* internalBuffer = buffer.getInternalBuffer(); + internalBuffer->writeHeader((uint32_t)GZippedFileProto::FILENAME, WIRE_TYPE_LENGTH_DELIMITED); + String8 usedFile(mFilenames[index]); + internalBuffer->writeRawVarint32(usedFile.size()); + for (size_t i = 0; i < usedFile.size(); i++) { + internalBuffer->writeRawByte(mFilenames[index][i]); + } + internalBuffer->writeHeader((uint32_t)GZippedFileProto::GZIPPED_DATA, + WIRE_TYPE_LENGTH_DELIMITED); + size_t editPos = internalBuffer->wp()->pos(); + internalBuffer->wp()->move(8); // reserve 8 bytes for the varint of the data size. + size_t dataBeginAt = internalBuffer->wp()->pos(); + VLOG("GZipSection '%s' editPos=%zd, dataBeginAt=%zd", this->name.string(), editPos, + dataBeginAt); + + status_t readStatus = buffer.readProcessedDataInStream( + fd, p2cPipe.writeFd(), c2pPipe.readFd(), this->timeoutMs, isSysfs(mFilenames[index])); + close(fd); // close the fd anyway. + + if (readStatus != NO_ERROR || buffer.timedOut()) { + ALOGW("GZipSection '%s' failed to read data from gzip: %s, timedout: %s", + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + kill_child(pid); + return readStatus; + } + + status_t gzipStatus = wait_child(pid); + if (gzipStatus != NO_ERROR) { + ALOGW("GZipSection '%s' abnormal child process: %s", this->name.string(), + strerror(-gzipStatus)); + return gzipStatus; + } + // Revisit the actual size from gzip result and edit the internal buffer accordingly. + size_t dataSize = buffer.size() - dataBeginAt; + internalBuffer->wp()->rewind()->move(editPos); + internalBuffer->writeRawVarint32(dataSize); + internalBuffer->copy(dataBeginAt, dataSize); + VLOG("GZipSection '%s' wrote %zd bytes in %d ms, dataSize=%zd", this->name.string(), + buffer.size(), (int)buffer.durationMs(), dataSize); + status_t err = write_report_requests(this->id, buffer, requests); + if (err != NO_ERROR) { + ALOGW("GZipSection '%s' failed writing: %s", this->name.string(), strerror(-err)); + return err; + } + + return NO_ERROR; +} // ================================================================================ struct WorkerThreadData : public virtual RefBase { @@ -340,7 +447,8 @@ WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec) WorkerThreadData::~WorkerThreadData() {} // ================================================================================ -WorkerThreadSection::WorkerThreadSection(int id) : Section(id) {} +WorkerThreadSection::WorkerThreadSection(int id, const int64_t timeoutMs) + : Section(id, timeoutMs) {} WorkerThreadSection::~WorkerThreadSection() {} @@ -457,42 +565,20 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { } // ================================================================================ -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) { - numOfArgs++; - } - va_end(copied_args); - - // allocate extra 1 for command and 1 for NULL terminator - mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2)); - - mCommand[0] = command; - name = command; - for (int i = 0; i < numOfArgs; i++) { - const char* arg = va_arg(args, const char*); - mCommand[i + 1] = arg; - name += " "; - name += arg; - } - mCommand[numOfArgs + 1] = NULL; -} - CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...) : Section(id, timeoutMs) { + name = command; va_list args; va_start(args, command); - init(command, args); + mCommand = varargs(command, args); va_end(args); } CommandSection::CommandSection(int id, const char* command, ...) : Section(id) { + name = command; va_list args; va_start(args, command); - init(command, args); + mCommand = varargs(command, args); va_end(args); } @@ -527,7 +613,7 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const { strerror(errno)); _exit(err); // exit with command error code } - pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe); + pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe); if (ihPid == -1) { ALOGW("CommandSection '%s' failed to fork", this->name.string()); return -errno; @@ -543,9 +629,8 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const { return readStatus; } - // 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. + // Waiting 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); if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) { @@ -644,7 +729,6 @@ static inline int32_t get4LE(uint8_t const* src) { } 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() @@ -655,15 +739,16 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { if (android_logger_open(loggers.get(), mLogID) == NULL) { ALOGW("LogSection %s: Can't get logger.", this->name.string()); - return err; + return NO_ERROR; } log_msg msg; log_time lastTimestamp(0); + status_t err = NO_ERROR; ProtoOutputStream proto; while (true) { // keeps reading until logd buffer is fully read. - status_t err = android_logger_list_read(loggers.get(), &msg); + 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 // err = -ive number, OS supplied error _except_ for -EAGAIN @@ -685,7 +770,7 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { lastTimestamp.tv_nsec = msg.entry_v1.nsec; // format a BinaryLogEntry - long long token = proto.start(LogProto::BINARY_LOGS); + uint64_t 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); @@ -695,7 +780,7 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { get4LE(reinterpret_cast(msg.msg()))); do { elem = android_log_read_next(context); - long long elemToken = proto.start(BinaryLogEntry::ELEMS); + uint64_t elemToken = proto.start(BinaryLogEntry::ELEMS); switch (elem.type) { case EVENT_TYPE_INT: proto.write(BinaryLogEntry::Elem::TYPE, @@ -747,7 +832,7 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { lastTimestamp.tv_nsec = entry.tv_nsec; // format a TextLogEntry - long long token = proto.start(LogProto::TEXT_LOGS); + uint64_t token = proto.start(LogProto::TEXT_LOGS); proto.write(TextLogEntry::SEC, (long long)entry.tv_sec); proto.write(TextLogEntry::NANOSEC, (long long)entry.tv_nsec); proto.write(TextLogEntry::PRIORITY, (int)entry.priority); @@ -764,3 +849,133 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { proto.flush(pipeWriteFd); return err; } + +// ================================================================================ + +TombstoneSection::TombstoneSection(int id, const char* type, const int64_t timeoutMs) + : WorkerThreadSection(id, timeoutMs), mType(type) { + name += "tombstone "; + name += type; +} + +TombstoneSection::~TombstoneSection() {} + +status_t TombstoneSection::BlockingCall(int pipeWriteFd) const { + std::unique_ptr proc(opendir("/proc"), closedir); + if (proc.get() == nullptr) { + ALOGE("opendir /proc failed: %s\n", strerror(errno)); + return -errno; + } + + const std::set hal_pids = get_interesting_hal_pids(); + + ProtoOutputStream proto; + struct dirent* d; + status_t err = NO_ERROR; + while ((d = readdir(proc.get()))) { + int pid = atoi(d->d_name); + if (pid <= 0) { + continue; + } + + const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid); + std::string exe; + if (!android::base::Readlink(link_name, &exe)) { + ALOGE("Can't read '%s': %s\n", link_name.c_str(), strerror(errno)); + continue; + } + + bool is_java_process; + if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") { + if (mType != "java") continue; + // Don't bother dumping backtraces for the zygote. + if (IsZygote(pid)) { + VLOG("Skipping Zygote"); + continue; + } + + is_java_process = true; + } else if (should_dump_native_traces(exe.c_str())) { + if (mType != "native") continue; + is_java_process = false; + } else if (hal_pids.find(pid) != hal_pids.end()) { + if (mType != "hal") continue; + is_java_process = false; + } else { + // Probably a native process we don't care about, continue. + VLOG("Skipping %d", pid); + continue; + } + + Fpipe dumpPipe; + if (!dumpPipe.init()) { + ALOGW("TombstoneSection '%s' failed to setup dump pipe", this->name.string()); + err = -errno; + break; + } + + const uint64_t start = Nanotime(); + pid_t child = fork(); + if (child < 0) { + ALOGE("Failed to fork child process"); + break; + } else if (child == 0) { + // This is the child process. + close(dumpPipe.readFd()); + const int ret = dump_backtrace_to_file_timeout( + pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, + is_java_process ? 5 : 20, dumpPipe.writeFd()); + if (ret == -1) { + if (errno == 0) { + ALOGW("Dumping failed for pid '%d', likely due to a timeout\n", pid); + } else { + ALOGE("Dumping failed for pid '%d': %s\n", pid, strerror(errno)); + } + } + if (close(dumpPipe.writeFd()) != 0) { + ALOGW("TombstoneSection '%s' failed to close dump pipe writeFd: %d", + this->name.string(), errno); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + close(dumpPipe.writeFd()); + // Parent process. + // Read from the pipe concurrently to avoid blocking the child. + FdBuffer buffer; + err = buffer.readFully(dumpPipe.readFd()); + if (err != NO_ERROR) { + ALOGW("TombstoneSection '%s' failed to read stack dump: %d", this->name.string(), err); + if (close(dumpPipe.readFd()) != 0) { + ALOGW("TombstoneSection '%s' failed to close dump pipe readFd: %s", + this->name.string(), strerror(errno)); + } + break; + } + + auto dump = std::make_unique(buffer.size()); + auto iterator = buffer.data(); + int i = 0; + while (iterator.hasNext()) { + dump[i] = iterator.next(); + i++; + } + long long token = proto.start(android::os::BackTraceProto::TRACES); + proto.write(android::os::BackTraceProto::Stack::PID, pid); + proto.write(android::os::BackTraceProto::Stack::DUMP, dump.get(), i); + proto.write(android::os::BackTraceProto::Stack::DUMP_DURATION_NS, + static_cast(Nanotime() - start)); + proto.end(token); + + if (close(dumpPipe.readFd()) != 0) { + ALOGW("TombstoneSection '%s' failed to close dump pipe readFd: %d", this->name.string(), + errno); + err = -errno; + break; + } + } + + proto.flush(pipeWriteFd); + return err; +} diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index d6446810c40fcabcda8a387d287f235700dd192a..19ef7ee74475e74d085368ea213f30dfe92dae9a 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -83,12 +83,27 @@ private: bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately }; +/** + * Section that reads in a file and gzips the content. + */ +class GZipSection : public Section { +public: + GZipSection(int id, const char* filename, ...); + virtual ~GZipSection(); + + virtual status_t Execute(ReportRequestSet* requests) const; + +private: + // It looks up the content from multiple files and stops when the first one is available. + const char** mFilenames; +}; + /** * Base class for sections that call a command that might need a timeout. */ class WorkerThreadSection : public Section { public: - WorkerThreadSection(int id); + WorkerThreadSection(int id, const int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS); virtual ~WorkerThreadSection(); virtual status_t Execute(ReportRequestSet* requests) const; @@ -111,8 +126,6 @@ public: private: const char** mCommand; - - void init(const char* command, va_list args); }; /** @@ -148,4 +161,18 @@ private: bool mBinary; }; +/** + * Section that gets data from tombstoned. + */ +class TombstoneSection : public WorkerThreadSection { +public: + TombstoneSection(int id, const char* type, const int64_t timeoutMs = 30000 /* 30 seconds */); + virtual ~TombstoneSection(); + + virtual status_t BlockingCall(int pipeWriteFd) const; + +private: + std::string mType; +}; + #endif // SECTIONS_H diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp index 2415860572fb22d18e9e13dc23cce183b516e03a..c869c7a8d1d4cafbb9c6acb6343bd173ad6e9c01 100644 --- a/cmds/incidentd/src/incidentd_util.cpp +++ b/cmds/incidentd/src/incidentd_util.cpp @@ -13,8 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define DEBUG false +#include "Log.h" + #include "incidentd_util.h" +#include + #include "section_list.h" const Privacy* get_privacy_of_section(int id) { @@ -50,4 +55,58 @@ 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 +int Fpipe::writeFd() const { return mWrite.get(); } + +pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output) { + // fork used in multithreaded environment, avoid adding unnecessary code in child process + pid_t pid = fork(); + if (pid == 0) { + if (TEMP_FAILURE_RETRY(dup2(input->readFd(), STDIN_FILENO)) < 0 || !input->close() || + TEMP_FAILURE_RETRY(dup2(output->writeFd(), STDOUT_FILENO)) < 0 || !output->close()) { + ALOGW("Can't setup stdin and stdout for command %s", cmd); + _exit(EXIT_FAILURE); + } + + /* make sure the child dies when incidentd dies */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + + execv(cmd, argv); + + ALOGW("%s failed in the child process: %s", cmd, strerror(errno)); + _exit(EXIT_FAILURE); // always exits with failure if any + } + // close the fds used in child process. + close(input->readFd()); + close(output->writeFd()); + return pid; +} + +// ================================================================================ +const char** varargs(const char* first, va_list rest) { + va_list copied_rest; + int numOfArgs = 1; // first is already count. + + va_copy(copied_rest, rest); + while (va_arg(copied_rest, const char*) != NULL) { + numOfArgs++; + } + va_end(copied_rest); + + // allocate extra 1 for NULL terminator + const char** ret = (const char**)malloc(sizeof(const char*) * (numOfArgs + 1)); + ret[0] = first; + for (int i = 1; i < numOfArgs; i++) { + const char* arg = va_arg(rest, const char*); + ret[i] = arg; + } + ret[numOfArgs] = NULL; + return ret; +} + +// ================================================================================ +const uint64_t NANOS_PER_SEC = 1000000000; +uint64_t Nanotime() { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return static_cast(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec); +} diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h index 09aa0404277ac50aae22f541d043c70f26496b1a..3f7df91e7e50411d8986e31cabc05412d72a9e83 100644 --- a/cmds/incidentd/src/incidentd_util.h +++ b/cmds/incidentd/src/incidentd_util.h @@ -20,12 +20,20 @@ #include +#include + #include "Privacy.h" using namespace android::base; +/** + * Looks up Privacy of a section in the auto-gen PRIVACY_POLICY_LIST; + */ const Privacy* get_privacy_of_section(int id); +/** + * This class wraps android::base::Pipe. + */ class Fpipe { public: Fpipe(); @@ -41,4 +49,20 @@ private: unique_fd mWrite; }; -#endif // INCIDENTD_UTIL_H \ No newline at end of file +/** + * Forks and exec a command with two pipes, one connects stdin for input, + * one connects stdout for output. It returns the pid of the child. + */ +pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output); + +/** + * Grabs varargs from stack and stores them in heap with NULL-terminated array. + */ +const char** varargs(const char* first, va_list rest); + +/** + * Returns the current monotonic clock time in nanoseconds. + */ +uint64_t Nanotime(); + +#endif // INCIDENTD_UTIL_H diff --git a/cmds/incidentd/testdata/kmsg.txt b/cmds/incidentd/testdata/kmsg.txt new file mode 100644 index 0000000000000000000000000000000000000000..a8e3c02faab5ca07f3f402056fd9b08ab3766961 --- /dev/null +++ b/cmds/incidentd/testdata/kmsg.txt @@ -0,0 +1,47 @@ +[0] bldr_log_init: bldr_log_base=0x83600000, bldr_log_size=458752 +B - 626409 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device +B - 729255 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device +B - 729285 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 9:0 +D - 104829 - APPSBL Image Loaded, Delta - (2498816 Bytes) +B - 729468 - SBL1, End +D - 643611 - SBL1, Delta +S - Flash Throughput, 129000 KB/s (4729638 Bytes, 36613 us) +S - DDR Frequency, 1017 MHz +0x400, 0x400 +B - 482296 - Basic DDR tests done +B - 544638 - clock_init, Start +D - 244 - clock_init, Delta +B - 544913 - HTC RPM DATARAM UPDATE info: Done +B - 545004 - Image Load, Start +B - 548359 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 5:0 +D - 3386 - QSEE Dev Config Image Loaded, Delta - (46232 Bytes) +B - 548725 - Image Load, Start +B - 550860 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 512:0 +D - 2166 - APDP Image Loaded, Delta - (7696 Bytes) +B - 550891 - Image Load, Start +B - 601612 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 7:0 +D - 50782 - QSEE Image Loaded, Delta - (1648640 Bytes) +B - 601704 - Image Load, Start +D - 244 - SEC Image Loaded, Delta - (4096 Bytes) +B - 602344 - 0x1310 = 0x24 +B - 602375 - is_above_vbat_weak = 1, pon_reasons (with usb_in checked) = 0x31 +B - 602466 - sbl1_efs_handle_cookies, Start +D - 91 - sbl1_efs_handle_cookies, Delta +B - 602558 - Image Load, Start +B - 613446 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 21:0 +D - 11010 - QHEE Image Loaded, Delta - (258280 Bytes) +B - 613568 - Image Load, Start +B - 624274 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 10:0 +D - 10736 - RPM Image Loaded, Delta - (224104 Bytes) +B - 624335 - Image Load, Start +D - 0 - STI Image Loaded, Delta - (0 Bytes) +B - 624548 - Image Load, Start +m_driver_init, Delta +B - 471804 - pm_sbl_chg + ******************** [ START SECOND] ******************** +^@B - 736605 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device +B - 839451 - [INFO][XBL]: Bypass appsbl verification on DEVELOPMENT device +B - 839482 - boot_elf_load_and_verify_image: boot_auth_compute_verify_hash for 9:0 +D - 104828 - APPSBL Image Loaded, Delta - (2498816 Bytes) +B - 839665 - SBL1, End +D - 753838 - SBL1, Delta diff --git a/cmds/incidentd/testdata/kmsg.txt.gz b/cmds/incidentd/testdata/kmsg.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..fba449f8922cc024607b1dc46f64760014a3c862 Binary files /dev/null and b/cmds/incidentd/testdata/kmsg.txt.gz differ diff --git a/cmds/incidentd/testdata/metadata.txt b/cmds/incidentd/testdata/metadata.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb79a3d6677aad1e7303db87196748eda6cb95c8 Binary files /dev/null and b/cmds/incidentd/testdata/metadata.txt differ diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index 955dbac72ebe1ed8220f964ef2b3a16a9f0f1d58..42d94f09b5089e617f44fc8bea57b646a2277f0a 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -191,7 +191,7 @@ TEST_F(ReporterTest, ReportMetadata) { reporter->batch.add(r); ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); - auto metadata = reporter->batch.metadata(); + IncidentMetadata metadata = reporter->batch.metadata(); EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest()); EXPECT_EQ(1, metadata.request_size()); EXPECT_TRUE(metadata.use_dropbox()); diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 026bf7419eb1e002296bc1854936de1541f6dbc1..55192d02acd3d1263ae31316eb616ef720b78a9f 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -39,10 +39,31 @@ using namespace android::binder; using namespace android::os; using namespace std; using ::testing::StrEq; +using ::testing::Test; using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStdout; // NOTICE: this test requires /system/bin/incident_helper is installed. +class SectionTest : public Test { +public: + virtual void SetUp() override { ASSERT_NE(tf.fd, -1); } + + void printDebugString(std::string s) { + fprintf(stderr, "size: %zu\n", s.length()); + for (size_t i = 0; i < s.length(); i++) { + char c = s[i]; + fprintf(stderr, "\\x%x", c); + } + fprintf(stderr, "\n"); + } + +protected: + TemporaryFile tf; + ReportRequestSet requests; + + const std::string kTestPath = GetExecutableDirectory(); + const std::string kTestDataPath = kTestPath + "/testdata/"; +}; class SimpleListener : public IIncidentReportStatusListener { public: @@ -58,10 +79,8 @@ protected: virtual IBinder* onAsBinder() override { return nullptr; }; }; -TEST(SectionTest, HeaderSection) { - TemporaryFile output2; +TEST_F(SectionTest, HeaderSection) { HeaderSection hs; - ReportRequestSet requests; IncidentReportArgs args1, args2; args1.addSection(1); @@ -77,7 +96,7 @@ TEST(SectionTest, HeaderSection) { args2.addHeader(head2); requests.add(new ReportRequest(args1, new SimpleListener(), -1)); - requests.add(new ReportRequest(args2, new SimpleListener(), output2.fd)); + requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd)); requests.setMainFd(STDOUT_FILENO); string content; @@ -87,28 +106,29 @@ TEST(SectionTest, HeaderSection) { "\x12\x3" "axe\n\x05\x12\x03pup")); - EXPECT_TRUE(ReadFileToString(output2.path, &content)); + EXPECT_TRUE(ReadFileToString(tf.path, &content)); EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); } -TEST(SectionTest, MetadataSection) { +TEST_F(SectionTest, MetadataSection) { MetadataSection ms; - ReportRequestSet requests; + const std::string testFile = kTestDataPath + "metadata.txt"; + std::string expect; + ASSERT_TRUE(ReadFileToString(testFile, &expect)); requests.setMainFd(STDOUT_FILENO); + requests.setMainDest(android::os::DEST_LOCAL); 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")); + // Notice message_lite.h ParseFromString doesn't work so we just match the bytes directly. + EXPECT_THAT(GetCapturedStdout(), StrEq(expect)); } -TEST(SectionTest, FileSection) { - TemporaryFile tf; +TEST_F(SectionTest, FileSection) { FileSection fs(REVERSE_PARSER, tf.path); - ReportRequestSet requests; - ASSERT_TRUE(tf.fd != -1); ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path)); requests.setMainFd(STDOUT_FILENO); @@ -120,66 +140,79 @@ TEST(SectionTest, FileSection) { EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai")); } -TEST(SectionTest, FileSectionTimeout) { - TemporaryFile tf; - // id -1 is timeout parser +TEST_F(SectionTest, FileSectionTimeout) { FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS); - ReportRequestSet requests; ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); } -TEST(SectionTest, CommandSectionConstructor) { +TEST_F(SectionTest, GZipSection) { + const std::string testFile = kTestDataPath + "kmsg.txt"; + const std::string testGzFile = testFile + ".gz"; + GZipSection gs(NOOP_PARSER, "/tmp/nonexist", testFile.c_str(), NULL); + + requests.setMainFd(tf.fd); + requests.setMainDest(android::os::DEST_LOCAL); + + ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); + std::string expect, gzFile, actual; + ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile)); + ASSERT_TRUE(ReadFileToString(tf.path, &actual)); + expect = "\x2\xC6\x6\n\"" + testFile + "\x12\x9F\x6" + gzFile; + EXPECT_THAT(actual, StrEq(expect)); +} + +TEST_F(SectionTest, GZipSectionNoFileFound) { + GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL); + requests.setMainFd(STDOUT_FILENO); + ASSERT_EQ(-1, gs.Execute(&requests)); +} + +TEST_F(SectionTest, CommandSectionConstructor) { CommandSection cs1(1, "echo", "\"this is a test\"", "ooo", NULL); CommandSection cs2(2, "single_command", NULL); CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL); CommandSection cs4(2, 43214, "single_command", NULL); - EXPECT_THAT(cs1.name.string(), StrEq("echo \"this is a test\" ooo")); + EXPECT_THAT(cs1.name.string(), StrEq("echo")); EXPECT_THAT(cs2.name.string(), StrEq("single_command")); EXPECT_EQ(3123, cs3.timeoutMs); EXPECT_EQ(43214, cs4.timeoutMs); - EXPECT_THAT(cs3.name.string(), StrEq("echo \"this is a test\" ooo")); + EXPECT_THAT(cs3.name.string(), StrEq("echo")); EXPECT_THAT(cs4.name.string(), StrEq("single_command")); } -TEST(SectionTest, CommandSectionEcho) { +TEST_F(SectionTest, CommandSectionEcho) { CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL); - ReportRequestSet requests; requests.setMainFd(STDOUT_FILENO); CaptureStdout(); ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba")); } -TEST(SectionTest, CommandSectionCommandTimeout) { +TEST_F(SectionTest, CommandSectionCommandTimeout) { CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL); - ReportRequestSet requests; ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); } -TEST(SectionTest, CommandSectionIncidentHelperTimeout) { +TEST_F(SectionTest, CommandSectionIncidentHelperTimeout) { CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL); - ReportRequestSet requests; requests.setMainFd(STDOUT_FILENO); ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); } -TEST(SectionTest, CommandSectionBadCommand) { +TEST_F(SectionTest, CommandSectionBadCommand) { CommandSection cs(NOOP_PARSER, "echoo", "about", NULL); - ReportRequestSet requests; ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests)); } -TEST(SectionTest, CommandSectionBadCommandAndTimeout) { +TEST_F(SectionTest, CommandSectionBadCommandAndTimeout) { CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL); - ReportRequestSet requests; // timeout will return first ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); } -TEST(SectionTest, LogSectionBinary) { +TEST_F(SectionTest, LogSectionBinary) { LogSection ls(1, LOG_ID_EVENTS); - ReportRequestSet requests; requests.setMainFd(STDOUT_FILENO); CaptureStdout(); ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); @@ -187,9 +220,8 @@ TEST(SectionTest, LogSectionBinary) { EXPECT_FALSE(results.empty()); } -TEST(SectionTest, LogSectionSystem) { +TEST_F(SectionTest, LogSectionSystem) { LogSection ls(1, LOG_ID_SYSTEM); - ReportRequestSet requests; requests.setMainFd(STDOUT_FILENO); CaptureStdout(); ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); @@ -197,12 +229,9 @@ TEST(SectionTest, LogSectionSystem) { EXPECT_FALSE(results.empty()); } -TEST(SectionTest, TestFilterPiiTaggedFields) { - TemporaryFile tf; +TEST_F(SectionTest, TestFilterPiiTaggedFields) { FileSection fs(NOOP_PARSER, tf.path); - ReportRequestSet requests; - ASSERT_TRUE(tf.fd != -1); ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); requests.setMainFd(STDOUT_FILENO); @@ -212,11 +241,9 @@ TEST(SectionTest, TestFilterPiiTaggedFields) { EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); } -TEST(SectionTest, TestBadFdRequest) { - TemporaryFile input; - FileSection fs(NOOP_PARSER, input.path); - ReportRequestSet requests; - ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); +TEST_F(SectionTest, TestBadFdRequest) { + FileSection fs(NOOP_PARSER, tf.path); + ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); IncidentReportArgs args; args.setAll(true); @@ -231,11 +258,9 @@ TEST(SectionTest, TestBadFdRequest) { EXPECT_EQ(badFdRequest->err, -EBADF); } -TEST(SectionTest, TestBadRequests) { - TemporaryFile input; - FileSection fs(NOOP_PARSER, input.path); - ReportRequestSet requests; - ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); +TEST_F(SectionTest, TestBadRequests) { + FileSection fs(NOOP_PARSER, tf.path); + ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); IncidentReportArgs args; args.setAll(true); @@ -244,16 +269,14 @@ TEST(SectionTest, TestBadRequests) { EXPECT_EQ(fs.Execute(&requests), -EBADF); } -TEST(SectionTest, TestMultipleRequests) { - TemporaryFile input, output1, output2, output3; - FileSection fs(NOOP_PARSER, input.path); - ReportRequestSet requests; +TEST_F(SectionTest, TestMultipleRequests) { + TemporaryFile output1, output2, output3; + FileSection fs(NOOP_PARSER, tf.path); - ASSERT_TRUE(input.fd != -1); ASSERT_TRUE(output1.fd != -1); ASSERT_TRUE(output2.fd != -1); ASSERT_TRUE(output3.fd != -1); - ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); + ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); IncidentReportArgs args1, args2, args3; args1.setAll(true); @@ -286,17 +309,15 @@ TEST(SectionTest, TestMultipleRequests) { EXPECT_THAT(content, StrEq("")); } -TEST(SectionTest, TestMultipleRequestsBySpec) { - TemporaryFile input, output1, output2, output3; - FileSection fs(NOOP_PARSER, input.path); - ReportRequestSet requests; +TEST_F(SectionTest, TestMultipleRequestsBySpec) { + TemporaryFile output1, output2, output3; + FileSection fs(NOOP_PARSER, tf.path); - ASSERT_TRUE(input.fd != -1); ASSERT_TRUE(output1.fd != -1); ASSERT_TRUE(output2.fd != -1); ASSERT_TRUE(output3.fd != -1); - ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); + ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); IncidentReportArgs args1, args2, args3; args1.setAll(true); @@ -328,4 +349,4 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { c = (char)STRING_FIELD_2.size(); EXPECT_TRUE(ReadFileToString(output3.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2)); -} \ No newline at end of file +} diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 87825f1619702830623ad9fd1e0ac05e2792538b..7f0a26c1714ea47f0fa006620bbf040ff981ff45 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -21,13 +21,16 @@ statsd_common_src := \ src/statsd_config.proto \ src/FieldValue.cpp \ src/stats_log_util.cpp \ - src/anomaly/AnomalyMonitor.cpp \ + src/anomaly/AlarmMonitor.cpp \ + src/anomaly/AlarmTracker.cpp \ src/anomaly/AnomalyTracker.cpp \ src/anomaly/DurationAnomalyTracker.cpp \ + src/anomaly/subscriber_util.cpp \ src/condition/CombinationConditionTracker.cpp \ src/condition/condition_util.cpp \ src/condition/SimpleConditionTracker.cpp \ src/condition/ConditionWizard.cpp \ + src/condition/StateTracker.cpp \ src/config/ConfigKey.cpp \ src/config/ConfigListener.cpp \ src/config/ConfigManager.cpp \ @@ -36,10 +39,7 @@ statsd_common_src := \ src/external/StatsCompanionServicePuller.cpp \ src/external/SubsystemSleepStatePuller.cpp \ src/external/ResourceHealthManagerPuller.cpp \ - src/external/CpuTimePerUidPuller.cpp \ - src/external/CpuTimePerUidFreqPuller.cpp \ - src/external/KernelUidCpuActiveTimeReader.cpp \ - src/external/KernelUidCpuClusterTimeReader.cpp \ + src/external/ResourceThermalManagerPuller.cpp \ src/external/StatsPullerManagerImpl.cpp \ src/external/puller_util.cpp \ src/logd/LogEvent.cpp \ @@ -66,7 +66,6 @@ statsd_common_src := \ src/subscriber/IncidentdReporter.cpp \ src/subscriber/SubscriberReporter.cpp \ src/HashableDimensionKey.cpp \ - src/guardrail/MemoryLeakTrackUtil.cpp \ src/guardrail/StatsdStats.cpp statsd_common_c_includes := \ @@ -99,7 +98,7 @@ statsd_common_shared_libraries := \ android.hardware.health@2.0 \ android.hardware.power@1.0 \ android.hardware.power@1.1 \ - libmemunreachable + android.hardware.thermal@1.0 # ========= # statsd @@ -127,7 +126,7 @@ else LOCAL_CFLAGS += \ -Os endif -LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static +LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes) LOCAL_C_INCLUDES += $(statsd_common_c_includes) @@ -170,7 +169,8 @@ LOCAL_SRC_FILES := \ src/atom_field_options.proto \ src/atoms.proto \ src/stats_log.proto \ - tests/AnomalyMonitor_test.cpp \ + tests/AlarmMonitor_test.cpp \ + tests/anomaly/AlarmTracker_test.cpp \ tests/anomaly/AnomalyTracker_test.cpp \ tests/ConfigManager_test.cpp \ tests/external/puller_util_test.cpp \ @@ -184,6 +184,7 @@ LOCAL_SRC_FILES := \ tests/FieldValue_test.cpp \ tests/condition/CombinationConditionTracker_test.cpp \ tests/condition/SimpleConditionTracker_test.cpp \ + tests/condition/StateTracker_test.cpp \ tests/metrics/OringDurationTracker_test.cpp \ tests/metrics/MaxDurationTracker_test.cpp \ tests/metrics/CountMetricProducer_test.cpp \ @@ -198,7 +199,9 @@ LOCAL_SRC_FILES := \ tests/e2e/MetricConditionLink_e2e_test.cpp \ tests/e2e/Attribution_e2e_test.cpp \ tests/e2e/GaugeMetric_e2e_test.cpp \ - tests/e2e/DimensionInCondition_e2e_test.cpp + tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \ + tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \ + tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) \ @@ -246,11 +249,32 @@ include $(CLEAR_VARS) LOCAL_MODULE := statsd_benchmark LOCAL_SRC_FILES := $(statsd_common_src) \ + src/atom_field_options.proto \ + src/atoms.proto \ + src/stats_log.proto \ benchmark/main.cpp \ benchmark/hello_world_benchmark.cpp \ benchmark/log_event_benchmark.cpp \ benchmark/stats_write_benchmark.cpp \ - benchmark/filter_value_benchmark.cpp + benchmark/filter_value_benchmark.cpp \ + benchmark/get_dimensions_for_condition_benchmark.cpp \ + benchmark/metric_util.cpp \ + benchmark/duration_metric_benchmark.cpp + +LOCAL_STATIC_LIBRARIES := \ + $(statsd_common_static_libraries) + +LOCAL_PROTOC_OPTIMIZE_TYPE := full + +LOCAL_PROTOC_FLAGS := \ + -Iexternal/protobuf/src + +LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ + libprotobuf-cpp-full + + +LOCAL_STATIC_JAVA_LIBRARIES := \ + platformprotoslite LOCAL_C_INCLUDES := $(statsd_common_c_includes) @@ -284,4 +308,4 @@ statsd_common_static_libraries:= statsd_common_shared_libraries:= -include $(call all-makefiles-under,$(LOCAL_PATH)) +include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS index 362d411d03a5a433a9e2d613fbb38dc2ee938137..13157505fc287d6af4f03f5f7072a95df20db746 100644 --- a/cmds/statsd/OWNERS +++ b/cmds/statsd/OWNERS @@ -1,6 +1,11 @@ bookatz@google.com +cjyu@google.com +dwchen@google.com jinyithu@google.com +joeo@google.com kwekua@google.com +singhtejinder@google.com stlafon@google.com yaochen@google.com yanglu@google.com +yro@google.com diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2631009c71f26f21647ecb7ccab7a491e865e079 --- /dev/null +++ b/cmds/statsd/benchmark/duration_metric_benchmark.cpp @@ -0,0 +1,320 @@ +/* + * 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" +#include "stats_log_util.h" +#include "metric_util.h" + +namespace android { +namespace os { +namespace statsd { + +using std::vector; + +static StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition( + DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); + dimensions->add_child()->set_field(2); // job name field. + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); + if (addExtraDimensionInCondition) { + syncDimension->add_child()->set_field(2 /* name field*/); + } + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(StringToId("CombinationPredicate")); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); + auto dimensionWhat = metric->mutable_dimensions_in_what(); + dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); + dimensionWhat->add_child()->set_field(2); // job name field. + *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +static StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition( + DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + *dimensions = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + dimensions->add_child()->set_field(2); // job name field. + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + if (addExtraDimensionInCondition) { + syncDimension->add_child()->set_field(2 /* name field*/); + } + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(StringToId("CombinationPredicate")); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); + *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +static void BM_DurationMetricNoLink(benchmark::State& state) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition( + DurationMetric::SUM, false); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + + 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> events; + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 11)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 40)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 102)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 450)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 650)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 100)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 640)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 650)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 10)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 200)); + 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(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 401)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + + sortLogEventsByTimestamp(&events); + + while (state.KeepRunning()) { + auto processor = CreateStatsLogProcessor( + bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + } +} + +BENCHMARK(BM_DurationMetricNoLink); + + +static void BM_DurationMetricLink(benchmark::State& state) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition( + DurationMetric::SUM, false); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + 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 attributions3 = { + CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector> events; + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 55)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 120)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 121)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 450)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 501)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 100)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500)); + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", + bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back( + CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back( + CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 110)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 300)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc", + bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 550)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 800)); + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs + 700)); + sortLogEventsByTimestamp(&events); + + while (state.KeepRunning()) { + auto processor = CreateStatsLogProcessor( + bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + } +} + +BENCHMARK(BM_DurationMetricLink); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp index b9ddf36d82955cbbc89c0d897e696ec31035c822..66c4defe7adb8f33cfeea53d89e4dbc64a56f266 100644 --- a/cmds/statsd/benchmark/filter_value_benchmark.cpp +++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp @@ -18,6 +18,7 @@ #include "FieldValue.h" #include "HashableDimensionKey.h" #include "logd/LogEvent.h" +#include "stats_log_util.h" namespace android { namespace os { @@ -25,17 +26,29 @@ namespace statsd { using std::vector; +static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matcher) { + AttributionNodeInternal node; + node.set_uid(100); + node.set_tag("LOCATION"); + + std::vector nodes = {node, node}; + event->write(nodes); + event->write(3.2f); + event->write("LOCATION"); + event->write((int64_t)990); + event->init(); + + field_matcher->set_field(1); + auto child = field_matcher->add_child(); + child->set_field(1); + child->set_position(FIRST); + child->add_child()->set_field(1); +} + 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); + createLogEventAndMatcher(&event, &field_matcher); std::vector matchers; translateFieldMatcher(field_matcher, &matchers); @@ -48,6 +61,22 @@ static void BM_FilterValue(benchmark::State& state) { BENCHMARK(BM_FilterValue); +static void BM_FilterValue2(benchmark::State& state) { + LogEvent event(1, 100000); + FieldMatcher field_matcher; + createLogEventAndMatcher(&event, &field_matcher); + + std::vector matchers; + translateFieldMatcher(field_matcher, &matchers); + + while (state.KeepRunning()) { + HashableDimensionKey output; + filterValues(matchers, event.getValues(), &output); + } +} + +BENCHMARK(BM_FilterValue2); + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a4403ed8282e2c7550faa09f8bea46eab719973 --- /dev/null +++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp @@ -0,0 +1,71 @@ +/* + * 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" +#include "stats_log_util.h" + +namespace android { +namespace os { +namespace statsd { + +using std::vector; + +static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) { + AttributionNodeInternal node; + node.set_uid(100); + node.set_tag("LOCATION"); + + std::vector nodes = {node, node}; + event->write(nodes); + event->write(3.2f); + event->write("LOCATION"); + event->write((int64_t)990); + event->init(); + + link->conditionId = 1; + + FieldMatcher field_matcher; + field_matcher.set_field(event->GetTagId()); + auto child = field_matcher.add_child(); + child->set_field(1); + child->set_position(FIRST); + child->add_child()->set_field(1); + + translateFieldMatcher(field_matcher, &link->metricFields); + field_matcher.set_field(event->GetTagId() + 1); + translateFieldMatcher(field_matcher, &link->conditionFields); +} + +static void BM_GetDimensionInCondition(benchmark::State& state) { + Metric2Condition link; + LogEvent event(1, 100000); + createLogEventAndLink(&event, &link); + + while (state.KeepRunning()) { + HashableDimensionKey output; + getDimensionForCondition(event.getValues(), link, &output); + } +} + +BENCHMARK(BM_GetDimensionInCondition); + + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b67764bd5130aa8c0408be8e84ebd60bc75a1752 --- /dev/null +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -0,0 +1,394 @@ +// 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 "metric_util.h" + +namespace android { +namespace os { +namespace statsd { + +AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(atomId); + return atom_matcher; +} + +AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name, + ScheduledJobStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(3); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateStartScheduledJobAtomMatcher() { + return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart", + ScheduledJobStateChanged::STARTED); +} + +AtomMatcher CreateFinishScheduledJobAtomMatcher() { + return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish", + ScheduledJobStateChanged::FINISHED); +} + +AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED); + return atom_matcher; +} + +AtomMatcher CreateUidProcessStateChangedAtomMatcher() { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId("UidProcessStateChanged")); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED); + return atom_matcher; +} + +AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, + WakelockStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(4); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateAcquireWakelockAtomMatcher() { + return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE); +} + +AtomMatcher CreateReleaseWakelockAtomMatcher() { + return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); +} + +AtomMatcher CreateScreenStateChangedAtomMatcher( + const string& name, android::view::DisplayStateEnum state) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(1); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateScreenTurnedOnAtomMatcher() { + return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", + android::view::DisplayStateEnum::DISPLAY_STATE_ON); +} + +AtomMatcher CreateScreenTurnedOffAtomMatcher() { + return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", + ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF); +} + +AtomMatcher CreateSyncStateChangedAtomMatcher( + const string& name, SyncStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(3); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateSyncStartAtomMatcher() { + return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON); +} + +AtomMatcher CreateSyncEndAtomMatcher() { + return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF); +} + +AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( + const string& name, ActivityForegroundStateChanged::Activity activity) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(4); // Activity field. + field_value_matcher->set_eq_int(activity); + return atom_matcher; +} + +AtomMatcher CreateMoveToBackgroundAtomMatcher() { + return CreateActivityForegroundStateChangedAtomMatcher( + "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND); +} + +AtomMatcher CreateMoveToForegroundAtomMatcher() { + return CreateActivityForegroundStateChangedAtomMatcher( + "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND); +} + +Predicate CreateScheduledJobPredicate() { + Predicate predicate; + predicate.set_id(StringToId("ScheduledJobRunningPredicate")); + predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish")); + return predicate; +} + +Predicate CreateBatterySaverModePredicate() { + Predicate predicate; + predicate.set_id(StringToId("BatterySaverIsOn")); + predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart")); + predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop")); + return predicate; +} + +Predicate CreateScreenIsOnPredicate() { + Predicate predicate; + predicate.set_id(StringToId("ScreenIsOn")); + predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff")); + return predicate; +} + +Predicate CreateScreenIsOffPredicate() { + Predicate predicate; + predicate.set_id(1111123); + predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn")); + return predicate; +} + +Predicate CreateHoldingWakelockPredicate() { + Predicate predicate; + predicate.set_id(StringToId("HoldingWakelock")); + predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock")); + return predicate; +} + +Predicate CreateIsSyncingPredicate() { + Predicate predicate; + predicate.set_id(33333333333333); + predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart")); + predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd")); + return predicate; +} + +Predicate CreateIsInBackgroundPredicate() { + Predicate predicate; + predicate.set_id(StringToId("IsInBackground")); + predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground")); + predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground")); + return predicate; +} + +void addPredicateToPredicateCombination(const Predicate& predicate, + Predicate* combinationPredicate) { + combinationPredicate->mutable_combination()->add_predicate(predicate.id()); +} + +FieldMatcher CreateAttributionUidDimensions(const int atomId, + const std::vector& positions) { + FieldMatcher dimensions; + dimensions.set_field(atomId); + for (const auto position : positions) { + auto child = dimensions.add_child(); + child->set_field(1); + child->set_position(position); + child->add_child()->set_field(1); + } + return dimensions; +} + +FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, + const std::vector& positions) { + FieldMatcher dimensions; + dimensions.set_field(atomId); + for (const auto position : positions) { + auto child = dimensions.add_child(); + child->set_field(1); + child->set_position(position); + child->add_child()->set_field(1); + child->add_child()->set_field(2); + } + return dimensions; +} + +FieldMatcher CreateDimensions(const int atomId, const std::vector& fields) { + FieldMatcher dimensions; + dimensions.set_field(atomId); + for (const int field : fields) { + dimensions.add_child()->set_field(field); + } + return dimensions; +} + +std::unique_ptr CreateScreenStateChangedEvent( + const android::view::DisplayStateEnum state, uint64_t timestampNs) { + auto event = std::make_unique(android::util::SCREEN_STATE_CHANGED, timestampNs); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr CreateScreenBrightnessChangedEvent( + int level, uint64_t timestampNs) { + auto event = std::make_unique(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs); + (event->write(level)); + event->init(); + return event; + +} + +std::unique_ptr CreateScheduledJobStateChangedEvent( + const std::vector& attributions, const string& jobName, + const ScheduledJobStateChanged::State state, uint64_t timestampNs) { + auto event = std::make_unique(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs); + event->write(attributions); + event->write(jobName); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr CreateStartScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs) { + return CreateScheduledJobStateChangedEvent( + attributions, name, ScheduledJobStateChanged::STARTED, timestampNs); +} + +// Create log event when scheduled job finishes. +std::unique_ptr CreateFinishScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs) { + return CreateScheduledJobStateChangedEvent( + attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs); +} + +std::unique_ptr CreateWakelockStateChangedEvent( + 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); + event->write(wakelockName); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr CreateAcquireWakelockEvent( + 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) { + return CreateWakelockStateChangedEvent( + attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs); +} + +std::unique_ptr CreateActivityForegroundStateChangedEvent( + const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) { + auto event = std::make_unique( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); + event->write(uid); + event->write("pkg_name"); + event->write("class_name"); + event->write(activity); + event->init(); + return event; +} + +std::unique_ptr CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { + return CreateActivityForegroundStateChangedEvent( + uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs); +} + +std::unique_ptr CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { + return CreateActivityForegroundStateChangedEvent( + uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs); +} + +std::unique_ptr CreateSyncStateChangedEvent( + 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); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr CreateSyncStartEvent( + 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) { + return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs); +} + +sp CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, + const ConfigKey& key) { + sp uidMap = new UidMap(); + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; + sp processor = new StatsLogProcessor( + uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){}); + processor->OnConfigUpdated(key, config); + return processor; +} + +AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) { + AttributionNodeInternal attribution; + attribution.set_uid(uid); + attribution.set_tag(tag); + return attribution; +} + +void sortLogEventsByTimestamp(std::vector> *events) { + std::sort(events->begin(), events->end(), + [](const std::unique_ptr& a, const std::unique_ptr& b) { + return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); + }); +} + +int64_t StringToId(const string& str) { + return static_cast(std::hash()(str)); +} + + +} // namespace statsd +} // namespace os +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h new file mode 100644 index 0000000000000000000000000000000000000000..9b28d60b38c07a0f7c282066ff129a884cef27a0 --- /dev/null +++ b/cmds/statsd/benchmark/metric_util.h @@ -0,0 +1,161 @@ +// 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/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "src/StatsLogProcessor.h" +#include "src/logd/LogEvent.h" +#include "statslog.h" + +namespace android { +namespace os { +namespace statsd { + +// Create AtomMatcher proto to simply match a specific atom type. +AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); + +// Create AtomMatcher proto for scheduled job state changed. +AtomMatcher CreateScheduledJobStateChangedAtomMatcher(); + +// Create AtomMatcher proto for starting a scheduled job. +AtomMatcher CreateStartScheduledJobAtomMatcher(); + +// Create AtomMatcher proto for a scheduled job is done. +AtomMatcher CreateFinishScheduledJobAtomMatcher(); + +// Create AtomMatcher proto for screen brightness state changed. +AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); + +// Create AtomMatcher proto for acquiring wakelock. +AtomMatcher CreateAcquireWakelockAtomMatcher(); + +// Create AtomMatcher proto for releasing wakelock. +AtomMatcher CreateReleaseWakelockAtomMatcher() ; + +// Create AtomMatcher proto for screen turned on. +AtomMatcher CreateScreenTurnedOnAtomMatcher(); + +// Create AtomMatcher proto for screen turned off. +AtomMatcher CreateScreenTurnedOffAtomMatcher(); + +// Create AtomMatcher proto for app sync turned on. +AtomMatcher CreateSyncStartAtomMatcher(); + +// Create AtomMatcher proto for app sync turned off. +AtomMatcher CreateSyncEndAtomMatcher(); + +// Create AtomMatcher proto for app sync moves to background. +AtomMatcher CreateMoveToBackgroundAtomMatcher(); + +// Create AtomMatcher proto for app sync moves to foreground. +AtomMatcher CreateMoveToForegroundAtomMatcher(); + +// Create Predicate proto for screen is off. +Predicate CreateScreenIsOffPredicate(); + +// Create Predicate proto for a running scheduled job. +Predicate CreateScheduledJobPredicate(); + +// Create Predicate proto for holding wakelock. +Predicate CreateHoldingWakelockPredicate(); + +// Create a Predicate proto for app syncing. +Predicate CreateIsSyncingPredicate(); + +// Create a Predicate proto for app is in background. +Predicate CreateIsInBackgroundPredicate(); + +// Add a predicate to the predicate combination. +void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); + +// Create dimensions from primitive fields. +FieldMatcher CreateDimensions(const int atomId, const std::vector& fields); + +// Create dimensions by attribution uid and tag. +FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, + const std::vector& positions); + +// Create dimensions by attribution uid only. +FieldMatcher CreateAttributionUidDimensions(const int atomId, + const std::vector& positions); + +// Create log event for screen state changed. +std::unique_ptr CreateScreenStateChangedEvent( + const android::view::DisplayStateEnum state, uint64_t timestampNs); + +// Create log event for screen brightness state changed. +std::unique_ptr CreateScreenBrightnessChangedEvent( + int level, uint64_t timestampNs); + +// Create log event when scheduled job starts. +std::unique_ptr CreateStartScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs); + +// Create log event when scheduled job finishes. +std::unique_ptr CreateFinishScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs); + +// Create log event for app moving to background. +std::unique_ptr CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); + +// Create log event for app moving to foreground. +std::unique_ptr CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs); + +// Create log event when the app sync starts. +std::unique_ptr CreateSyncStartEvent( + 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); + +// Create log event when the app sync ends. +std::unique_ptr CreateAppCrashEvent( + const int uid, uint64_t timestampNs); + +// Create log event for acquiring wakelock. +std::unique_ptr CreateAcquireWakelockEvent( + 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); + +// 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 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, + const ConfigKey& key); + +// Util function to sort the log events by timestamp. +void sortLogEventsByTimestamp(std::vector> *events); + +int64_t StringToId(const string& str); + +} // namespace statsd +} // namespace os +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index b541612f73df39e7002aef18e1bc707d4ae9c85e..0c9b7016eaff1a971f145ac48a65087341c4e1ca 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -205,6 +205,29 @@ bool Value::operator<(const Value& that) const { } } +bool equalDimensions(const std::vector& dimension_a, + const std::vector& dimension_b) { + bool eq = dimension_a.size() == dimension_b.size(); + for (size_t i = 0; eq && i < dimension_a.size(); ++i) { + if (dimension_b[i] != dimension_a[i]) { + eq = false; + } + } + return eq; +} + +bool HasPositionANY(const FieldMatcher& matcher) { + if (matcher.has_position() && matcher.position() == Position::ANY) { + return true; + } + for (const auto& child : matcher.child()) { + if (HasPositionANY(child)) { + return true; + } + } + 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 index 21f30e288c257b3c0a2c9d27e62bd5bdce69bf20..0e3ae06033e75c3efc8e2e57309937e3009ff59b 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -41,6 +41,7 @@ int32_t encodeMatcherMask(int32_t mask[], int32_t depth); 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. @@ -201,9 +202,9 @@ public: * } * * 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 + * First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f + * Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f + * Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f * * [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 @@ -217,6 +218,14 @@ struct Matcher { const Field mMatcher; const int32_t mMask; + inline const Field& getMatcher() const { + return mMatcher; + } + + inline int32_t getMask() const { + return mMask; + } + bool hasAnyPositionMatcher(int* prefix) const { if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) { (*prefix) = mMatcher.getPrefix(2); @@ -224,8 +233,20 @@ struct Matcher { } return false; } + + inline bool operator!=(const Matcher& that) const { + return mMatcher != that.getMatcher() || mMask != that.getMask(); + } + + inline bool operator==(const Matcher& that) const { + return mMatcher == that.mMatcher && mMask == that.mMask; + } }; +inline Matcher getSimpleMatcher(int32_t tag, size_t field) { + return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000); +} + /** * A wrapper for a union type to contain multiple types of values. * @@ -315,11 +336,16 @@ struct FieldValue { Value mValue; }; +bool HasPositionANY(const FieldMatcher& matcher); + bool isAttributionUidField(const FieldValue& value); void translateFieldMatcher(const FieldMatcher& matcher, std::vector* output); bool isAttributionUidField(const Field& field, const Value& value); + +bool equalDimensions(const std::vector& dimension_a, + const std::vector& dimension_b); } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 1502a00abee92f390fd685213845c17866265ebc..d0c83119dc43e66337a303169ee0ffaf21fccdb1 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -59,6 +59,33 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { return JenkinsHashWhiten(hash); } +bool filterValues(const vector& matcherFields, const vector& values, + HashableDimensionKey* output) { + for (size_t i = 0; i < matcherFields.size(); ++i) { + const auto& matcher = matcherFields[i]; + bool found = false; + for (const auto& value : values) { + // TODO: potential optimization here to break early because all fields are naturally + // sorted. + if (value.mField.matches(matcher)) { + output->addValue(value); + output->mutableValue(i)->mField.setTag(value.mField.getTag()); + output->mutableValue(i)->mField.setField(value.mField.getField() & matcher.mMask); + found = true; + break; + } + } + + if (!found) { + VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(), + matcher.mMatcher.getField()); + return false; + } + } + + return true; +} + // 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, @@ -166,23 +193,23 @@ void filterGaugeValues(const std::vector& matcherFields, } } -void getDimensionForCondition(const LogEvent& event, Metric2Condition links, - vector* conditionDimension) { +void getDimensionForCondition(const std::vector& eventValues, + const Metric2Condition& links, + HashableDimensionKey* 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; - } + filterValues(links.metricFields, eventValues, conditionDimension); - 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()); - } + size_t count = conditionDimension->getValues().size(); + if (count != links.conditionFields.size()) { + // ALOGE("WTF condition link is bad"); + return; + } + + for (size_t i = 0; i < count; i++) { + conditionDimension->mutableValue(i)->mField.setField( + links.conditionFields[i].mMatcher.getField()); + conditionDimension->mutableValue(i)->mField.setTag( + links.conditionFields[i].mMatcher.getTag()); } } diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 89fe317834d81b9981d4c39723d23c75b8014729..4cfed883ec0753e2ae7d0b046c3c869be65b355c 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -40,7 +40,7 @@ public: mValues = values; } - HashableDimensionKey(){}; + HashableDimensionKey() {}; HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){}; @@ -65,10 +65,6 @@ public: 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; @@ -104,6 +100,10 @@ class MetricDimensionKey { return mDimensionKeyInCondition; } + inline void setDimensionKeyInCondition(const HashableDimensionKey& key) { + mDimensionKeyInCondition = key; + } + bool hasDimensionKeyInCondition() const { return mDimensionKeyInCondition.getValues().size() > 0; } @@ -112,9 +112,6 @@ class MetricDimensionKey { bool operator<(const MetricDimensionKey& that) const; - inline const char* c_str() const { - return toString().c_str(); - } private: HashableDimensionKey mDimensionKeyInWhat; HashableDimensionKey mDimensionKeyInCondition; @@ -134,6 +131,9 @@ android::hash_t hashDimension(const HashableDimensionKey& key); */ bool filterValues(const std::vector& matcherFields, const std::vector& values, std::vector* output); +// This function is used when there is at most one output dimension key. (no ANY matcher) +bool filterValues(const std::vector& matcherFields, const std::vector& values, + HashableDimensionKey* output); /** * Filter the values from FieldValues using the matchers. @@ -144,8 +144,9 @@ bool filterValues(const std::vector& matcherFields, const std::vector& matchers, const std::vector& values, std::vector* output); -void getDimensionForCondition(const LogEvent& event, Metric2Condition links, - std::vector* conditionDimension); +void getDimensionForCondition(const std::vector& eventValues, + const Metric2Condition& links, + HashableDimensionKey* conditionDimension); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 3c9dd68c1cc29697ff8813a32f1c1c27004a7d7c..02d4dc985e84cc4c6532087bc5b0d26a965f07a1 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -66,13 +66,16 @@ const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4; #define STATS_DATA_DIR "/data/misc/stats-data" StatsLogProcessor::StatsLogProcessor(const sp& uidMap, - const sp& anomalyMonitor, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, const long timeBaseSec, const std::function& sendBroadcast) : mUidMap(uidMap), - mAnomalyMonitor(anomalyMonitor), + mAnomalyAlarmMonitor(anomalyAlarmMonitor), + mPeriodicAlarmMonitor(periodicAlarmMonitor), mSendBroadcast(sendBroadcast), - mTimeBaseSec(timeBaseSec) { + mTimeBaseSec(timeBaseSec), + mLastLogTimestamp(0) { StatsPullerManager statsPullerManager; statsPullerManager.SetTimeBaseSec(mTimeBaseSec); } @@ -82,10 +85,19 @@ StatsLogProcessor::~StatsLogProcessor() { void StatsLogProcessor::onAnomalyAlarmFired( const uint64_t timestampNs, - unordered_set, SpHash> anomalySet) { + unordered_set, SpHash> alarmSet) { std::lock_guard lock(mMetricsMutex); for (const auto& itr : mMetricsManagers) { - itr.second->onAnomalyAlarmFired(timestampNs, anomalySet); + itr.second->onAnomalyAlarmFired(timestampNs, alarmSet); + } +} +void StatsLogProcessor::onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash> alarmSet) { + + std::lock_guard lock(mMetricsMutex); + for (const auto& itr : mMetricsManagers) { + itr.second->onPeriodicAlarmFired(timestampNs, alarmSet); } } @@ -133,9 +145,12 @@ void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) { } } -// TODO: what if statsd service restarts? How do we know what logs are already processed before? void StatsLogProcessor::OnLogEvent(LogEvent* event) { std::lock_guard lock(mMetricsMutex); + if (event->GetElapsedTimestampNs() < mLastLogTimestamp) { + return; + } + mLastLogTimestamp = event->GetElapsedTimestampNs(); StatsdStats::getInstance().noteAtomLogged( event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC); @@ -170,7 +185,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { std::lock_guard lock(mMetricsMutex); VLOG("Updated configuration for key %s", key.ToString().c_str()); - sp newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap); + sp newMetricsManager = + new MetricsManager(key, config, mTimeBaseSec, mUidMap, + mAnomalyAlarmMonitor, mPeriodicAlarmMonitor); auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) { ALOGE("Can't accept more configs!"); @@ -179,7 +196,6 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig if (newMetricsManager->isConfigValid()) { mUidMap->OnConfigUpdated(key); - newMetricsManager->setAnomalyMonitor(mAnomalyMonitor); if (newMetricsManager->shouldAddUidMapListener()) { // We have to add listener after the MetricsManager is constructed because it's // not safe to create wp or sp from this pointer inside its constructor. @@ -233,14 +249,14 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t ProtoOutputStream proto; // Start of ConfigKey. - long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); + uint64_t configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid()); proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId()); proto.end(configKeyToken); // End of ConfigKey. // Start of ConfigMetricsReport (reports). - long long reportsToken = + uint64_t reportsToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS); int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); @@ -314,9 +330,7 @@ void StatsLogProcessor::flushIfNecessaryLocked( mLastByteSizeTimes[key] = timestampNs; if (totalBytes > 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(timestampNs, &proto); + metricsManager.dropData(timestampNs); StatsdStats::getInstance().noteDataDropped(key); VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) { @@ -341,7 +355,6 @@ void StatsLogProcessor::WriteDataToDisk() { const ConfigKey& key = pair.first; vector 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, (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 144430639d9f44cf44dbe05ab0c4108053dff643..8db82006d082f80a73ae02e0153a1798c801fdf8 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -34,7 +34,8 @@ namespace statsd { class StatsLogProcessor : public ConfigListener { public: - StatsLogProcessor(const sp& uidMap, const sp& anomalyMonitor, + StatsLogProcessor(const sp& uidMap, const sp& anomalyAlarmMonitor, + const sp& subscriberTriggerAlarmMonitor, const long timeBaseSec, const std::function& sendBroadcast); virtual ~StatsLogProcessor(); @@ -48,10 +49,15 @@ public: void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector* outData); - /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */ + /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */ void onAnomalyAlarmFired( const uint64_t timestampNs, - unordered_set, SpHash> anomalySet); + unordered_set, SpHash> alarmSet); + + /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */ + void onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash> alarmSet); /* Flushes data to disk. Data on memory will be gone after written to disk. */ void WriteDataToDisk(); @@ -76,7 +82,9 @@ private: StatsPullerManager mStatsPullerManager; - sp mAnomalyMonitor; + sp mAnomalyAlarmMonitor; + + sp mPeriodicAlarmMonitor; void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs, vector* outData); @@ -98,12 +106,13 @@ private: const long mTimeBaseSec; + int64_t mLastLogTimestamp; + long mLastPullerCacheClearTimeSec = 0; FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); - FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); @@ -114,10 +123,21 @@ private: FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice); FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink); - FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink); - FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink); - FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition); + + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition); + + + + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition); + }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index c27b130678cd141aee605ab2da2656b888d4c0dd..c5dfc4488c1b9730333192ee3086b168c5be719a 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -22,7 +22,6 @@ #include "android-base/stringprintf.h" #include "config/ConfigKey.h" #include "config/ConfigManager.h" -#include "guardrail/MemoryLeakTrackUtil.h" #include "guardrail/StatsdStats.h" #include "storage/StorageManager.h" #include "subscriber/SubscriberReporter.h" @@ -50,49 +49,77 @@ namespace statsd { constexpr const char* kPermissionDump = "android.permission.DUMP"; #define STATS_SERVICE_DIR "/data/misc/stats-service" -// ====================================================================== /** * Watches for the death of the stats companion (system process). */ class CompanionDeathRecipient : public IBinder::DeathRecipient { public: - CompanionDeathRecipient(const sp& anomalyMonitor); + CompanionDeathRecipient(const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, + const sp& processor) + : mAnomalyAlarmMonitor(anomalyAlarmMonitor), + mPeriodicAlarmMonitor(periodicAlarmMonitor), + mProcessor(processor) {} virtual void binderDied(const wp& who); private: - const sp mAnomalyMonitor; + sp mAnomalyAlarmMonitor; + sp mPeriodicAlarmMonitor; + sp mProcessor; }; -CompanionDeathRecipient::CompanionDeathRecipient(const sp& anomalyMonitor) - : mAnomalyMonitor(anomalyMonitor) { -} - void CompanionDeathRecipient::binderDied(const wp& who) { ALOGW("statscompanion service died"); - mAnomalyMonitor->setStatsCompanionService(nullptr); + mProcessor->WriteDataToDisk(); + mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); + mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); SubscriberReporter::getInstance().setStatsCompanionService(nullptr); } -// ====================================================================== StatsService::StatsService(const sp& handlerLooper) - : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS)) -{ + : mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, + [](const sp& sc, int64_t timeMillis) { + if (sc != nullptr) { + sc->setAnomalyAlarm(timeMillis); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } + }, + [](const sp& sc) { + if (sc != nullptr) { + sc->cancelAnomalyAlarm(); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } + })), + mPeriodicAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, + [](const sp& sc, int64_t timeMillis) { + if (sc != nullptr) { + sc->setAlarmForSubscriberTriggering(timeMillis); + StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); + } + }, + [](const sp& sc) { + if (sc != nullptr) { + sc->cancelAlarmForSubscriberTriggering(); + StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); + } + + })) { mUidMap = new UidMap(); StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); - 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); - } + mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor, + 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); @@ -423,6 +450,13 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, VectorUpdateConfig(ConfigKey(uid, StrToInt64(name)), config); + mConfigManager->UpdateConfig(ConfigKey(uid, configID), config); } else { if (argCount == 2) { cmd_remove_all_configs(out); @@ -607,15 +641,14 @@ status_t StatsService::cmd_remove_all_configs(FILE* out) { } status_t StatsService::cmd_dump_memory_info(FILE* out) { - std::string s = dumpMemInfo(100); - fprintf(out, "Memory Info\n"); - fprintf(out, "%s", s.c_str()); + fprintf(out, "meminfo not available.\n"); return NO_ERROR; } status_t StatsService::cmd_clear_puller_cache(FILE* out) { IPCThreadState* ipc = IPCThreadState::self(); - VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); + 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); @@ -670,18 +703,40 @@ Status StatsService::informAnomalyAlarmFired() { return Status::fromExceptionCode(Status::EX_SECURITY, "Only system uid can call informAnomalyAlarmFired"); } + uint64_t currentTimeSec = getElapsedRealtimeSec(); - std::unordered_set, SpHash> anomalySet = - mAnomalyMonitor->popSoonerThan(static_cast(currentTimeSec)); - if (anomalySet.size() > 0) { + std::unordered_set, SpHash> alarmSet = + mAnomalyAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + if (alarmSet.size() > 0) { VLOG("Found an anomaly alarm that fired."); - mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet); + mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet); } else { VLOG("Cannot find an anomaly alarm that fired. Perhaps it was recently cancelled."); } return Status::ok(); } +Status StatsService::informAlarmForSubscriberTriggeringFired() { + VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); + + if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { + return Status::fromExceptionCode( + Status::EX_SECURITY, + "Only system uid can call informAlarmForSubscriberTriggeringFired"); + } + + uint64_t currentTimeSec = time(nullptr); + std::unordered_set, SpHash> alarmSet = + mPeriodicAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + if (alarmSet.size() > 0) { + VLOG("Found periodic alarm fired."); + mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet); + } else { + ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled."); + } + return Status::ok(); +} + Status StatsService::informPollAlarmFired() { VLOG("StatsService::informPollAlarmFired was called"); @@ -766,10 +821,12 @@ Status StatsService::statsCompanionReady() { "statscompanion unavailable despite it contacting statsd!"); } VLOG("StatsService::statsCompanionReady linking to statsCompanion."); - IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor)); - mAnomalyMonitor->setStatsCompanionService(statsCompanion); + IInterface::asBinder(statsCompanion) + ->linkToDeath(new CompanionDeathRecipient( + mAnomalyAlarmMonitor, mPeriodicAlarmMonitor, mProcessor)); + mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion); + mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion); SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion); - return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 9690de702c248c877785b34a8ef0ff681103f468..02b61244b3bbbd4c152be474e5da9a7dc08c3e56 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -18,7 +18,7 @@ #define STATS_SERVICE_H #include "StatsLogProcessor.h" -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" #include "config/ConfigManager.h" #include "external/StatsPullerManager.h" #include "packages/UidMap.h" @@ -58,6 +58,8 @@ public: virtual Status statsCompanionReady(); virtual Status informAnomalyAlarmFired(); virtual Status informPollAlarmFired(); + virtual Status informAlarmForSubscriberTriggeringFired(); + virtual Status informAllUidData(const vector& uid, const vector& version, const vector& app); virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version); @@ -172,11 +174,6 @@ private: */ status_t cmd_print_stats(FILE* out, const Vector& args); - /** - * Print the event log. - */ - status_t cmd_print_stats_log(FILE* out, const Vector& args); - /** * Print the event log. */ @@ -213,10 +210,10 @@ private: */ status_t cmd_dump_memory_info(FILE* out); - /* + /* * Clear all puller cached data */ - status_t cmd_clear_puller_cache(FILE* out); + status_t cmd_clear_puller_cache(FILE* out); /** * Update a configuration. @@ -244,9 +241,14 @@ private: sp mProcessor; /** - * The anomaly detector. + * The alarm monitor for anomaly detection. + */ + const sp mAnomalyAlarmMonitor; + + /** + * The alarm monitor for alarms to directly trigger subscriber. */ - const sp mAnomalyMonitor; + const sp mPeriodicAlarmMonitor; /** * Whether this is an eng build. diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp similarity index 73% rename from cmds/statsd/src/anomaly/AnomalyMonitor.cpp rename to cmds/statsd/src/anomaly/AlarmMonitor.cpp index ca34dc6d87fd3e4aaa443de9ee0ad87dd26f0355..78f0c2b0953778b947ca8a13ed7fe7cc6229ee5b 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp +++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp @@ -17,21 +17,24 @@ #define DEBUG false #include "Log.h" -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" #include "guardrail/StatsdStats.h" namespace android { namespace os { namespace statsd { -AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec) - : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) { -} +AlarmMonitor::AlarmMonitor( + uint32_t minDiffToUpdateRegisteredAlarmTimeSec, + const std::function&, int64_t)>& updateAlarm, + const std::function&)>& cancelAlarm) + : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec), + mUpdateAlarm(updateAlarm), + mCancelAlarm(cancelAlarm) {} -AnomalyMonitor::~AnomalyMonitor() { -} +AlarmMonitor::~AlarmMonitor() {} -void AnomalyMonitor::setStatsCompanionService(sp statsCompanionService) { +void AlarmMonitor::setStatsCompanionService(sp statsCompanionService) { std::lock_guard lock(mLock); sp tmpForLock = mStatsCompanionService; mStatsCompanionService = statsCompanionService; @@ -40,13 +43,13 @@ void AnomalyMonitor::setStatsCompanionService(sp statsCo return; } VLOG("Creating link to statsCompanionService"); - const sp top = mPq.top(); + const sp top = mPq.top(); if (top != nullptr) { updateRegisteredAlarmTime_l(top->timestampSec); } } -void AnomalyMonitor::add(sp alarm) { +void AlarmMonitor::add(sp alarm) { std::lock_guard lock(mLock); if (alarm == nullptr) { ALOGW("Asked to add a null alarm."); @@ -66,7 +69,7 @@ void AnomalyMonitor::add(sp alarm) { } } -void AnomalyMonitor::remove(sp alarm) { +void AlarmMonitor::remove(sp alarm) { std::lock_guard lock(mLock); if (alarm == nullptr) { ALOGW("Asked to remove a null alarm."); @@ -89,13 +92,13 @@ void AnomalyMonitor::remove(sp alarm) { // More efficient than repeatedly calling remove(mPq.top()) since it batches the // updates to the registered alarm. -unordered_set, SpHash> AnomalyMonitor::popSoonerThan( +unordered_set, SpHash> AlarmMonitor::popSoonerThan( uint32_t timestampSec) { VLOG("Removing alarms with time <= %u", timestampSec); - unordered_set, SpHash> oldAlarms; + unordered_set, SpHash> oldAlarms; std::lock_guard lock(mLock); - for (sp t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; + for (sp t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; t = mPq.top()) { oldAlarms.insert(t); mPq.pop(); // remove t @@ -113,25 +116,19 @@ unordered_set, SpHash> AnomalyMonitor::popS return oldAlarms; } -void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { +void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { VLOG("Updating reg alarm time to %u", timestampSec); mRegisteredAlarmTimeSec = timestampSec; - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec)); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } + mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec)); } -void AnomalyMonitor::cancelRegisteredAlarmTime_l() { +void AlarmMonitor::cancelRegisteredAlarmTime_l() { VLOG("Cancelling reg alarm."); mRegisteredAlarmTimeSec = 0; - if (mStatsCompanionService != nullptr) { - mStatsCompanionService->cancelAnomalyAlarm(); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } + mCancelAlarm(mStatsCompanionService); } -int64_t AnomalyMonitor::secToMs(uint32_t timeSec) { +int64_t AlarmMonitor::secToMs(uint32_t timeSec) { return ((int64_t)timeSec) * 1000; } diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h similarity index 77% rename from cmds/statsd/src/anomaly/AnomalyMonitor.h rename to cmds/statsd/src/anomaly/AlarmMonitor.h index 7acc7904bb5706882f000e798ce60ce175483b82..3badb1faece625b974bfe0f29816bf213b6f4c36 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.h +++ b/cmds/statsd/src/anomaly/AlarmMonitor.h @@ -41,33 +41,34 @@ namespace statsd { * threshold. * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106. */ -struct AnomalyAlarm : public RefBase { - AnomalyAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { +struct InternalAlarm : public RefBase { + InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) { } const uint32_t timestampSec; - /** AnomalyAlarm a is smaller (higher priority) than b if its timestamp is sooner. */ + /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */ struct SmallerTimestamp { - bool operator()(sp a, sp b) const { + bool operator()(sp a, sp b) const { return (a->timestampSec < b->timestampSec); } }; }; -// TODO: Rename this file to AnomalyAlarmMonitor. /** - * Manages alarms for Anomaly Detection. + * Manages internal alarms that may get registered with the AlarmManager. */ -class AnomalyMonitor : public RefBase { +class AlarmMonitor : public RefBase { public: /** * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs * from the registered alarm by more than this amount, update the registered * alarm. */ - AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec); - ~AnomalyMonitor(); + AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec, + const std::function&, int64_t)>& updateAlarm, + const std::function&)>& cancelAlarm); + ~AlarmMonitor(); /** * Tells AnomalyMonitor what IStatsCompanionService to use and, if @@ -80,20 +81,20 @@ public: /** * Adds the given alarm (reference) to the queue. */ - void add(sp alarm); + void add(sp alarm); /** * Removes the given alarm (reference) from the queue. * Note that alarm comparison is reference-based; if another alarm exists * with the same timestampSec, that alarm will still remain in the queue. */ - void remove(sp alarm); + void remove(sp alarm); /** * Returns and removes all alarms whose timestamp <= the given timestampSec. * Always updates the registered alarm if return is non-empty. */ - unordered_set, SpHash> popSoonerThan( + unordered_set, SpHash> popSoonerThan( uint32_t timestampSec); /** @@ -119,7 +120,7 @@ private: /** * Priority queue of alarms, prioritized by soonest alarm.timestampSec. */ - indexed_priority_queue mPq; + indexed_priority_queue mPq; /** * Binder interface for communicating with StatsCompanionService. @@ -146,6 +147,13 @@ private: /** Converts uint32 timestamp in seconds to a Java long in msec. */ int64_t secToMs(uint32_t timeSec); + + // Callback function to update the alarm via StatsCompanionService. + std::function, int64_t)> mUpdateAlarm; + + // Callback function to cancel the alarm via StatsCompanionService. + std::function)> mCancelAlarm; + }; } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb283838afd76172b46a2a65bc572ee8718e3891 --- /dev/null +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -0,0 +1,79 @@ +/* + * 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 // STOPSHIP if true +#include "Log.h" + +#include "anomaly/AlarmTracker.h" +#include "anomaly/subscriber_util.h" +#include "HashableDimensionKey.h" +#include "stats_util.h" +#include "storage/StorageManager.h" + +#include +#include + +namespace android { +namespace os { +namespace statsd { + +AlarmTracker::AlarmTracker(uint64_t startMillis, + const Alarm& alarm, const ConfigKey& configKey, + const sp& alarmMonitor) + : mAlarmConfig(alarm), + mConfigKey(configKey), + mAlarmMonitor(alarmMonitor) { + VLOG("AlarmTracker() called"); + mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC; + mInternalAlarm = new InternalAlarm{static_cast(mAlarmSec)}; + mAlarmMonitor->add(mInternalAlarm); +} + +AlarmTracker::~AlarmTracker() { + VLOG("~AlarmTracker() called"); + if (mInternalAlarm != nullptr) { + mAlarmMonitor->remove(mInternalAlarm); + } +} + +void AlarmTracker::addSubscription(const Subscription& subscription) { + mSubscriptions.push_back(subscription); +} + +uint64_t AlarmTracker::findNextAlarmSec(uint64_t currentTimeSec) { + int periodsForward = (currentTimeSec - mAlarmSec) * MS_PER_SEC / mAlarmConfig.period_millis(); + return mAlarmSec + (periodsForward + 1) * mAlarmConfig.period_millis() / MS_PER_SEC; +} + +void AlarmTracker::informAlarmsFired( + const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) { + if (firedAlarms.empty() || firedAlarms.find(mInternalAlarm) == firedAlarms.end()) { + return; + } + if (!mSubscriptions.empty()) { + triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey, + mSubscriptions); + } + firedAlarms.erase(mInternalAlarm); + mAlarmSec = findNextAlarmSec(timestampNs / NS_PER_SEC); + mInternalAlarm = new InternalAlarm{static_cast(mAlarmSec)}; + mAlarmMonitor->add(mInternalAlarm); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h new file mode 100644 index 0000000000000000000000000000000000000000..d59dacaa1b6983b0fe9dc74483316d1d9e469294 --- /dev/null +++ b/cmds/statsd/src/anomaly/AlarmTracker.h @@ -0,0 +1,76 @@ +/* + * 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 + +#include "AlarmMonitor.h" +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm + +#include +#include +#include + +using android::os::IStatsCompanionService; + +namespace android { +namespace os { +namespace statsd { + +class AlarmTracker : public virtual RefBase { +public: + AlarmTracker(uint64_t startMillis, + const Alarm& alarm, const ConfigKey& configKey, + const sp& subscriberAlarmMonitor); + + virtual ~AlarmTracker(); + + void onAlarmFired(); + + void addSubscription(const Subscription& subscription); + + void informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms); + +protected: + uint64_t findNextAlarmSec(uint64_t currentTimeMillis); + + // statsd_config.proto Alarm message that defines this tracker. + const Alarm mAlarmConfig; + + // A reference to the Alarm's config key. + const ConfigKey& mConfigKey; + + // The subscriptions that depend on this alarm. + std::vector mSubscriptions; + + // Alarm monitor. + sp mAlarmMonitor; + + // The current expected alarm time in seconds. + uint64_t mAlarmSec; + + // The current alarm. + sp mInternalAlarm; + + FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index c40eb812f949e0aed3cec3affdc30f362d5e2f37..133f33b15405e0b16fc8a6a3c1c17d5fe07be780 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "subscriber_util.h" #include "external/Perfetto.h" #include "guardrail/StatsdStats.h" #include "subscriber/IncidentdReporter.h" @@ -58,6 +59,12 @@ void AnomalyTracker::resetStorage() { } size_t AnomalyTracker::index(int64_t bucketNum) const { + if (bucketNum < 0) { + // To support this use-case, we can easily modify index to wrap around. But currently + // AnomalyTracker should never need this, so if it happens, it's a bug we should log. + // TODO: Audit this. + ALOGE("index() was passed a negative bucket number (%lld)!", (long long)bucketNum); + } return bucketNum % mNumOfPastBuckets; } @@ -71,9 +78,7 @@ void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) { // The past packets are ancient. Empty out old mPastBuckets[i] values and reset // mSumOverPastBuckets. if (latestPastBucketNum - mMostRecentBucketNum >= mNumOfPastBuckets) { - mPastBuckets.clear(); - mPastBuckets.resize(mNumOfPastBuckets); - mSumOverPastBuckets.clear(); + resetStorage(); } else { for (int64_t i = std::max(0LL, (long long)(mMostRecentBucketNum - mNumOfPastBuckets + 1)); i <= latestPastBucketNum - mNumOfPastBuckets; i++) { @@ -149,7 +154,7 @@ void AnomalyTracker::addBucketToSum(const shared_ptr& bucket) { int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const { - if (mNumOfPastBuckets == 0) { + if (mNumOfPastBuckets == 0 || bucketNum < 0) { return 0; } @@ -192,7 +197,8 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDim if (!mSubscriptions.empty()) { if (mAlert.has_id()) { - ALOGI("An anomaly (%lld) has occurred! Informing subscribers.", mAlert.id()); + ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", mAlert.id(), + key.toString().c_str()); informSubscribers(key); } else { ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers."); @@ -231,40 +237,7 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, } void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) { - VLOG("informSubscribers called."); - if (mSubscriptions.empty()) { - // The config just wanted to log the anomaly. That's fine. - VLOG("No Subscriptions were associated with the alert."); - return; - } - - 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: - if (!GenerateIncidentReport(subscription.incidentd_details(), mAlert, mConfigKey)) { - ALOGW("Failed to generate incident report."); - } - break; - case Subscription::SubscriberInformationCase::kPerfettoDetails: - if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) { - ALOGW("Failed to generate prefetto traces."); - } - break; - case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: - SubscriberReporter::getInstance().alertBroadcastSubscriber(mConfigKey, subscription, - key); - break; - default: - break; - } - } + triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions); } } // namespace statsd diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 3be959d14109e69c138c62015b1692f548e339c7..d27dee843b22a0ebb6808b7976f7fe0dd0aad7cd 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -23,7 +23,7 @@ #include #include -#include "AnomalyMonitor.h" +#include "AlarmMonitor.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert #include "stats_util.h" // HashableDimensionKey and DimToValMap @@ -64,9 +64,9 @@ public: void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, 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. + // Init the AlarmMonitor which is shared across anomaly trackers. + virtual void setAlarmMonitor(const sp& alarmMonitor) { + return; // Base AnomalyTracker class has no need for the AlarmMonitor. } // Helper function to return the sum value of past buckets at given dimension. @@ -92,11 +92,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, - unordered_set, SpHash>& firedAlarms) { - return; // The base AnomalyTracker class doesn't have alarms. + // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor. + virtual void informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) { + return; // The base AnomalyTracker class doesn't have alarms. } protected: @@ -111,7 +110,7 @@ protected: // Number of past buckets. One less than the total number of buckets needed // for the anomaly detection (since the current bucket is not in the past). - int mNumOfPastBuckets; + const int mNumOfPastBuckets; // The existing bucket list. std::vector> mPastBuckets; diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp index 3ba943c310bbddf97875251f145db22065cee086..31d50be7ec262818541a13c908d8d047b93354a3 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp @@ -24,8 +24,9 @@ namespace android { namespace os { namespace statsd { -DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey) - : AnomalyTracker(alert, configKey) { +DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, + const sp& alarmMonitor) + : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) { } DurationAnomalyTracker::~DurationAnomalyTracker() { @@ -59,10 +60,10 @@ void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey, VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period"); timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1; } - sp alarm = new AnomalyAlarm{timestampSec}; + sp alarm = new InternalAlarm{timestampSec}; mAlarms.insert({dimensionKey, alarm}); - if (mAnomalyMonitor != nullptr) { - mAnomalyMonitor->add(alarm); + if (mAlarmMonitor != nullptr) { + mAlarmMonitor->add(alarm); } } @@ -70,8 +71,8 @@ void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) { auto itr = mAlarms.find(dimensionKey); if (itr != mAlarms.end()) { mAlarms.erase(dimensionKey); - if (mAnomalyMonitor != nullptr) { - mAnomalyMonitor->remove(itr->second); + if (mAlarmMonitor != nullptr) { + mAlarmMonitor->remove(itr->second); } } } @@ -86,16 +87,16 @@ void DurationAnomalyTracker::stopAllAlarms() { } } -void DurationAnomalyTracker::informAlarmsFired( - const uint64_t& timestampNs, - unordered_set, SpHash>& firedAlarms) { +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 + // seldomly called. The alternative would be having InternalAlarms store information about the // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that // is rarely ever called. - unordered_map> matchedAlarms; + unordered_map> matchedAlarms; for (const auto& kv : mAlarms) { if (firedAlarms.count(kv.second) > 0) { matchedAlarms.insert({kv.first, kv.second}); diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h index 15aef29bc644c97fa4814e94f1ca7c79b42b8f9d..51186df3e93d9749c1b163c8bca48a5ed1f5d300 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h @@ -16,7 +16,7 @@ #pragma once -#include "AnomalyMonitor.h" +#include "AlarmMonitor.h" #include "AnomalyTracker.h" namespace android { @@ -27,7 +27,8 @@ using std::unordered_map; class DurationAnomalyTracker : public virtual AnomalyTracker { public: - DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey); + DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey, + const sp& alarmMonitor); virtual ~DurationAnomalyTracker(); @@ -40,11 +41,6 @@ public: // Stop all the alarms owned by this tracker. void stopAllAlarms(); - // Init the AnomalyMonitor which is shared across anomaly trackers. - void setAnomalyMonitor(const sp& anomalyMonitor) override { - mAnomalyMonitor = anomalyMonitor; - } - // Declares the anomaly when the alarm expired given the current timestamp. void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey, const uint64_t& timestampNs); @@ -53,17 +49,16 @@ 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, - unordered_set, SpHash>& firedAlarms) override; + void informAlarmsFired(const uint64_t& timestampNs, + unordered_set, SpHash>& firedAlarms) override; protected: // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they // are still active. - std::unordered_map> mAlarms; + std::unordered_map> mAlarms; // Anomaly alarm monitor. - sp mAnomalyMonitor; + sp mAlarmMonitor; // Resets all bucket data. For use when all the data gets stale. void resetStorage() override; diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e796d19efd93573035593d55cb608a71f48be838 --- /dev/null +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -0,0 +1,75 @@ +/* + * 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 // STOPSHIP if true +#include "Log.h" + +#include +#include +#include + +#include "external/Perfetto.h" +#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" +#include "subscriber/IncidentdReporter.h" +#include "subscriber/SubscriberReporter.h" + +namespace android { +namespace os { +namespace statsd { + +void triggerSubscribers(const int64_t rule_id, + const MetricDimensionKey& dimensionKey, + const ConfigKey& configKey, + const std::vector& subscriptions) { + VLOG("informSubscribers called."); + if (subscriptions.empty()) { + VLOG("No Subscriptions were associated."); + return; + } + + for (const Subscription& subscription : subscriptions) { + 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: + if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) { + ALOGW("Failed to generate incident report."); + } + break; + case Subscription::SubscriberInformationCase::kPerfettoDetails: + if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) { + ALOGW("Failed to generate prefetto traces."); + } + break; + case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails: + SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription, + dimensionKey); + break; + default: + break; + } + } +} + + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h b/cmds/statsd/src/anomaly/subscriber_util.h similarity index 58% rename from cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h rename to cmds/statsd/src/anomaly/subscriber_util.h index 90236ae0076232e154c67f6db658b40154fa238b..dba8981a72aad2e0d7af6564e8f1bcc961be0f56 100644 --- a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h +++ b/cmds/statsd/src/anomaly/subscriber_util.h @@ -16,26 +16,18 @@ #pragma once -#include -#include "StatsPuller.h" +#include "config/ConfigKey.h" +#include "HashableDimensionKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { namespace os { namespace statsd { -/** - * Reads /proc/uid_cputime/show_uid_stat which has the line format: - * - * uid: user_time_micro_seconds system_time_micro_seconds - * - * This provides the time a UID's processes spent executing in user-space and kernel-space. - * The file contains a monotonically increasing count of time for a single boot. - */ -class KernelUidCpuClusterTimeReader : public StatsPuller { - public: - KernelUidCpuClusterTimeReader(); - bool PullInternal(vector>* data) override; -}; +void triggerSubscribers(const int64_t rule_id, + const MetricDimensionKey& dimensionKey, + const ConfigKey& configKey, + const std::vector& subscriptions); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 04ebfcd0f4dec236701c5a4e6a52e299c8f46e6f..9bfbd38f89323a5ddc76c69ca6d7d47e097efef0 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -72,7 +72,7 @@ message Atom { BatteryLevelChanged battery_level_changed = 30; ChargingStateChanged charging_state_changed = 31; PluggedStateChanged plugged_state_changed = 32; - DeviceTemperatureReported device_temperature_reported = 33; + // TODO: 33 is blank, but is available for use. DeviceOnStatusChanged device_on_status_changed = 34; WakeupAlarmOccurred wakeup_alarm_occurred = 35; KernelWakeupReported kernel_wakeup_reported = 36; @@ -101,11 +101,16 @@ message Atom { OverlayStateChanged overlay_state_changed = 59; ForegroundServiceStateChanged foreground_service_state_changed = 60; CallStateChanged call_state_changed = 61; + KeyguardStateChanged keyguard_state_changed = 62; + KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63; + KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64; + AppDied app_died=65; + ResourceConfigurationChanged resource_configuration_changed = 66; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } // Pulled events will start at field 10000. - // Next: 10021 + // Next: 10022 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -128,7 +133,11 @@ message Atom { DiskSpace disk_space = 10018; RemainingBatteryCapacity remaining_battery_capacity = 10019; FullBatteryCapacity full_battery_capacity = 10020; + Temperature temperature = 10021; } + + // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above + // 100,000 are reserved for non-AOSP (e.g. OEMs) to use. } /** @@ -536,17 +545,6 @@ message PluggedStateChanged { optional android.os.BatteryPluggedStateEnum state = 1; } -/** - * Logs the temperature of the device, in tenths of a degree Celsius. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message DeviceTemperatureReported { - // Temperature in tenths of a degree C. - optional int32 temperature = 1; -} - // TODO: Define this more precisely. // TODO: Log the ON state somewhere. It isn't currently logged anywhere. /** @@ -756,6 +754,155 @@ message CallStateChanged { optional bool external_call = 4; } +/** + * Logs keyguard state. The keyguard is the lock screen. + * + * Logged from: + * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java + */ +message KeyguardStateChanged { + enum State { + UNKNOWN = 0; + // The keyguard is hidden when the phone is unlocked. + HIDDEN = 1; + // The keyguard is shown when the phone is locked (screen turns off). + SHOWN= 2; + // The keyguard is occluded when something is overlaying the keyguard. + // Eg. Opening the camera while on the lock screen. + OCCLUDED = 3; + } + optional State state = 1; +} + +/** + * Logs keyguard bouncer state. The bouncer is a part of the keyguard, and + * prompts the user to enter a password (pattern, pin, etc). + * + * Logged from: + * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java + */ + +message KeyguardBouncerStateChanged { + enum State { + UNKNOWN = 0; + // Bouncer is hidden, either as a result of successfully entering the + // password, screen timing out, or user going back to lock screen. + HIDDEN = 1; + // This is when the user is being prompted to enter the password. + SHOWN = 2; + } + optional State state = 1; +} + +/** + * Logs the result of entering a password into the keyguard bouncer. + * + * Logged from: + * frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java + */ +message KeyguardBouncerPasswordEntered { + enum BouncerResult { + UNKNOWN = 0; + // The password entered was incorrect. + FAILURE = 1; + // The password entered was correct. + SUCCESS = 2; + } + optional BouncerResult result = 1; +} + +/* + * Logs changes to the configuration of the device. The configuration is defined + * in frameworks/base/core/java/android/content/res/Configuration.java + * More documentation is at https://d.android.com/reference/android/content/res/Configuration.html + * Please go there to interpret the possible values each field can be. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message ResourceConfigurationChanged { + // Bit mask of color capabilities of the screen. + // Contains information about the color gamut and hdr mode of the screen. + // See: https://d.android.com/reference/android/content/res/Configuration.html#colorMode + optional int32 colorMode = 1; + + // The target screen density being rendered to. + // See: https://d.android.com/reference/android/content/res/Configuration.html#densityDpi + optional int32 densityDpi = 2; + + // Current user preference for the scaling factor for fonts, + // relative to the base density scaling. + // See: https://d.android.com/reference/android/content/res/Configuration.html#fontScale + optional float fontScale = 3; + + // Flag indicating whether the hard keyboard is hidden. + // See: https://d.android.com/reference/android/content/res/Configuration.html#hardKeyboardHidden + optional int32 hardKeyboardHidden = 4; + + // The type of keyboard attached to the device. + // See: https://d.android.com/reference/android/content/res/Configuration.html#keyboard + optional int32 keyboard = 5; + + // Flag indicating whether any keyboard is available. Takes soft keyboards into account. + // See: https://d.android.com/reference/android/content/res/Configuration.html#keyboardHidden + optional int32 keyboardHideen = 6; + + // IMSI MCC (Mobile Country Code), corresponding to mcc resource qualifier. + // 0 if undefined. + // See: https://d.android.com/reference/android/content/res/Configuration.html#mcc + optional int32 mcc = 7; + + // IMSI MNC (Mobile Network Code), corresponding to mnc resource qualifier. + // 0 if undefined. Note: the actual MNC may be 0, to check for this use the + // MNC_ZERO symbol defined in Configuration.java. + // See: https://d.android.com/reference/android/content/res/Configuration.html#mnc + optional int32 mnc = 8; + + // The kind of navigation available on the device. + // See: https://developer.android.com/reference/android/content/res/Configuration.html#navigation + optional int32 navigation = 9; + + // Flag indicating whether the navigation is available. + // See: https://d.android.com/reference/android/content/res/Configuration.html#navigationHidden + optional int32 navigationHidden = 10; + + // Overall orientation of the screen. + // See: https://d.android.com/reference/android/content/res/Configuration.html#orientation + optional int32 orientation = 11; + + // The current height of the available screen space, in dp units. + // See: https://d.android.com/reference/android/content/res/Configuration.html#screenHeightDp + optional int32 screenHeightDp = 12; + + // Bit mask of overall layout of the screen. + // Contains information about screen size, whether the screen is wider/taller + // than normal, whether the screen layout is right-tl-left or left-to-right, + // and whether the screen has a rounded shape. + // See: https://d.android.com/reference/android/content/res/Configuration.html#screenLayout + optional int32 screenLayout = 13; + + // Current width of the available screen space, in dp units. + // See: https://d.android.com/reference/android/content/res/Configuration.html#screenWidthDp + optional int32 screenWidthDp = 14; + + // The smallest screen size an application will see in normal operation. + // This is the smallest value of both screenWidthDp and screenHeightDp + // in portrait and landscape. + // See: https://d.android.com/reference/android/content/res/Configuration.html#smallestScreenWidthDp + optional int32 smallestScreenWidthDp = 15; + + // The type of touch screen attached to the device. + // See: https://d.android.com/reference/android/content/res/Configuration.html#touchscreen + optional int32 touchscreen = 16; + + // Bit mask of the ui mode. + // Contains information about the overall ui mode of the device. + // Eg: NORMAL, DESK, CAR, TELEVISION, WATCH, VR_HEADSET + // Also contains information about whether the device is in night mode. + // See: https://d.android.com/reference/android/content/res/Configuration.html#uiMode + optional int32 uiMode = 17; +} + /** * Logs the duration of a davey (jank of >=700ms) when it occurs * @@ -763,8 +910,11 @@ message CallStateChanged { * frameworks/base/libs/hwui/JankTracker.cpp */ message DaveyOccurred { + // The UID that logged this atom. + optional int32 uid = 1; + // Amount of time it took to render the frame. Should be >=700ms. - optional int64 jank_duration_millis = 1; + optional int64 jank_duration_millis = 2; } /** @@ -1179,6 +1329,17 @@ message LmkKillOccurred { optional int64 swap_in_bytes = 8; } +/* + * Logs when the ActivityManagerService detects that an app died. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message AppDied { + // timestamp(elapsedRealtime) of record creation + optional uint64 timestamp_millis = 1; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -1328,7 +1489,7 @@ message CpuTimePerFreq { * Note that isolated process uid time should be attributed to host uids. */ message CpuTimePerUid { - optional uint64 uid = 1; + optional int32 uid = 1; optional uint64 user_time_millis = 2; optional uint64 sys_time_millis = 3; } @@ -1339,8 +1500,8 @@ message CpuTimePerUid { * For each uid, we order the time by descending frequencies. */ message CpuTimePerUidFreq { - optional uint64 uid = 1; - optional uint64 freq_idx = 2; + optional int32 uid = 1; + optional uint32 freq_index = 2; optional uint64 time_millis = 3; } @@ -1473,10 +1634,8 @@ message SystemUptime { * The file contains a monotonically increasing count of time for a single boot. */ message CpuActiveTime { - optional uint64 uid = 1; - optional uint32 cluster_number = 2; - optional uint64 idx = 3; - optional uint64 time_millis = 4; + optional int32 uid = 1; + optional uint64 time_millis = 2; } /** @@ -1489,8 +1648,8 @@ message CpuActiveTime { * The file contains a monotonically increasing count of time for a single boot. */ message CpuClusterTime { - optional uint64 uid = 1; - optional uint64 idx = 2; + optional int32 uid = 1; + optional int32 cluster_index = 2; optional uint64 time_millis = 3; } @@ -1508,7 +1667,8 @@ message DiskSpace { /** * Pulls battery coulomb counter, which is the remaining battery charge in uAh. - * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp + * Pulled from: + * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp */ message RemainingBatteryCapacity { optional int32 charge_uAh = 1; @@ -1516,8 +1676,26 @@ message RemainingBatteryCapacity { /** * Pulls battery capacity, which is the battery capacity when full in uAh. - * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp + * Pulled from: + * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp */ message FullBatteryCapacity { optional int32 capacity_uAh = 1; -} \ No newline at end of file +} + +/** + * Pulls the temperature of various parts of the device, in Celsius. + * + * Pulled from: + * frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp + */ +message Temperature { + // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY. + optional android.os.TemperatureTypeEnum sensor_location = 1; + + // The name of the temperature source. Eg. CPU0 + optional string sensor_name = 2; + + // Temperature in degrees C. + optional float temperature_C = 3; +} diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 13a2b7be9152bb3bfe74dd3eacd16cd020b1bc47..3661d2b9a7687267a8118acde9ddfb2c3a0bcde0 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -91,6 +91,9 @@ bool CombinationConditionTracker::init(const vector& allConditionConf if (allConditionTrackers[childIndex]->isSliced()) { setSliced(true); + mSlicedChildren.push_back(childIndex); + } else { + mUnSlicedChildren.push_back(childIndex); } mChildren.push_back(childIndex); mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(), @@ -107,13 +110,19 @@ bool CombinationConditionTracker::init(const vector& allConditionConf void CombinationConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector>& allConditions, - const std::vector& dimensionFields, vector& conditionCache, + const std::vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, + 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) { allConditions[childIndex]->isConditionMet(conditionParameters, allConditions, - dimensionFields, conditionCache, + dimensionFields, + isSubOutputDimensionFields, + isPartialLink, + conditionCache, dimensionsKeySet); } } @@ -150,7 +159,11 @@ void CombinationConditionTracker::evaluateCondition( nonSlicedConditionCache[mIndex] = mNonSlicedConditionState; conditionChangedCache[mIndex] = nonSlicedChanged; + mUnSlicedPart = newCondition; } else { + mUnSlicedPart = evaluateCombinationCondition( + mUnSlicedChildren, mLogicalOperation, nonSlicedConditionCache); + for (const int childIndex : mChildren) { // If any of the sliced condition in children condition changes, the combination // condition may be changed too. @@ -168,13 +181,14 @@ void CombinationConditionTracker::evaluateCondition( ConditionState CombinationConditionTracker::getMetConditionDimension( const std::vector>& allConditions, const std::vector& dimensionFields, + const bool isSubOutputDimensionFields, 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) { conditionCache[childIndex] = conditionCache[childIndex] | allConditions[childIndex]->getMetConditionDimension( - allConditions, dimensionFields, dimensionsKeySet); + allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet); } evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) { @@ -183,6 +197,18 @@ ConditionState CombinationConditionTracker::getMetConditionDimension( return conditionCache[mIndex]; } +bool CombinationConditionTracker::equalOutputDimensions( + const std::vector>& allConditions, + const vector& dimensions) const { + if (mSlicedChildren.size() != 1 || + mSlicedChildren.front() >= (int)allConditions.size() || + mLogicalOperation != LogicalOperation::AND) { + return false; + } + const sp& slicedChild = allConditions.at(mSlicedChildren.front()); + return slicedChild->equalOutputDimensions(allConditions, dimensions); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index ba185f6661ecc2e0f290cbd3268f01101a1081a4..481cb200d8e6da0ab05461dc97f9920385fbba76 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -44,14 +44,61 @@ public: void isConditionMet(const ConditionKey& conditionParameters, const std::vector>& allConditions, const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, std::vector& conditionCache, std::unordered_set& dimensionsKeySet) const override; ConditionState getMetConditionDimension( const std::vector>& allConditions, const vector& dimensionFields, + const bool isSubOutputDimensionFields, std::unordered_set& dimensionsKeySet) const override; + // Only one child predicate can have dimension. + const std::set* getChangedToTrueDimensions( + const std::vector>& allConditions) const override { + for (const auto& child : mChildren) { + auto result = allConditions[child]->getChangedToTrueDimensions(allConditions); + if (result != nullptr) { + return result; + } + } + return nullptr; + } + + // Only one child predicate can have dimension. + const std::set* getChangedToFalseDimensions( + const std::vector>& allConditions) const override { + for (const auto& child : mChildren) { + auto result = allConditions[child]->getChangedToFalseDimensions(allConditions); + if (result != nullptr) { + return result; + } + } + return nullptr; + } + + bool IsSimpleCondition() const override { return false; } + + bool IsChangedDimensionTrackable() const override { + return mLogicalOperation == LogicalOperation::AND && mSlicedChildren.size() == 1; + } + + bool equalOutputDimensions( + const std::vector>& allConditions, + const vector& dimensions) const override; + + void getTrueSlicedDimensions( + const std::vector>& allConditions, + std::set* dimensions) const override { + if (mSlicedChildren.size() == 1) { + return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions( + allConditions, dimensions); + } + } + + private: LogicalOperation mLogicalOperation; @@ -60,6 +107,10 @@ private: // map the name to object. We don't want to store smart pointers to children, because it // increases the risk of circular dependency and memory leak. std::vector mChildren; + + std::vector mSlicedChildren; + std::vector mUnSlicedChildren; + }; } // namespace statsd diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 2612a9a06da1bef76b36c56b724bbe029f612e0e..1f4266b61cdf6d89bc63c95ec5d6f2e5850436b1 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -84,18 +84,28 @@ public: // condition. // [allConditions]: all condition trackers. This is needed because the condition evaluation is // done recursively + // [dimensionFields]: the needed dimension fields which should be all or subset of the condition + // tracker output dimension. + // [isSubOutputDimensionFields]: true if the needed dimension fields which is strictly subset of + // the condition tracker output dimension. + // [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields + // in the condition tracker output dimension. // [conditionCache]: the cache holding the condition evaluation values. // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination // condition, it assumes that only one child predicate is sliced. virtual void isConditionMet( const ConditionKey& conditionParameters, const std::vector>& allConditions, - const vector& dimensionFields, std::vector& conditionCache, + const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, + std::vector& conditionCache, std::unordered_set& dimensionsKeySet) const = 0; virtual ConditionState getMetConditionDimension( const std::vector>& allConditions, const vector& dimensionFields, + const bool isSubOutputDimensionFields, std::unordered_set& dimensionsKeySet) const = 0; // return the list of LogMatchingTracker index that this ConditionTracker uses. @@ -107,10 +117,35 @@ public: mSliced = mSliced | sliced; } - bool isSliced() const { + inline bool isSliced() const { return mSliced; } + virtual const std::set* getChangedToTrueDimensions( + const std::vector>& allConditions) const = 0; + virtual const std::set* getChangedToFalseDimensions( + const std::vector>& allConditions) const = 0; + + inline int64_t getConditionId() const { + return mConditionId; + } + + virtual void getTrueSlicedDimensions( + const std::vector>& allConditions, + std::set* dimensions) const = 0; + + virtual bool IsChangedDimensionTrackable() const = 0; + + virtual bool IsSimpleCondition() const = 0; + + virtual bool equalOutputDimensions( + const std::vector>& allConditions, + const vector& dimensions) const = 0; + + inline ConditionState getUnSlicedPartConditionState() const { + return mUnSlicedPart; + } + protected: const int64_t mConditionId; @@ -126,6 +161,7 @@ protected: ConditionState mNonSlicedConditionState; bool mSliced; + ConditionState mUnSlicedPart; }; } // namespace statsd diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index c8722c362fe69802340462d14e90051153948a27..23a9d371145e7ae6440797dc7181db263e7fb8d1 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -26,19 +26,58 @@ using std::vector; ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters, const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, std::unordered_set* dimensionKeySet) { vector cache(mAllConditions.size(), ConditionState::kNotEvaluated); mAllConditions[index]->isConditionMet( - parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet); + parameters, mAllConditions, dimensionFields, isSubOutputDimensionFields, isPartialLink, + cache, *dimensionKeySet); return cache[index]; } ConditionState ConditionWizard::getMetConditionDimension( const int index, const vector& dimensionFields, + const bool isSubOutputDimensionFields, std::unordered_set* dimensionsKeySet) const { return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields, - *dimensionsKeySet); + isSubOutputDimensionFields, + *dimensionsKeySet); +} + +const set* ConditionWizard::getChangedToTrueDimensions( + const int index) const { + return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions); +} + +const set* ConditionWizard::getChangedToFalseDimensions( + const int index) const { + return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions); +} + +bool ConditionWizard::IsChangedDimensionTrackable(const int index) { + if (index >= 0 && index < (int)mAllConditions.size()) { + return mAllConditions[index]->IsChangedDimensionTrackable(); + } else { + return false; + } +} + +bool ConditionWizard::IsSimpleCondition(const int index) { + if (index >= 0 && index < (int)mAllConditions.size()) { + return mAllConditions[index]->IsSimpleCondition(); + } else { + return false; + } +} + +bool ConditionWizard::equalOutputDimensions(const int index, const vector& dimensions) { + if (index >= 0 && index < (int)mAllConditions.size()) { + return mAllConditions[index]->equalOutputDimensions(mAllConditions, dimensions); + } else { + return false; + } } } // namespace statsd diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 4831d5622a3db5ef594111fac098b52d30e062d7..a6f88af4b4d689237d32565a41aa60ea6fd489c8 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -41,12 +41,31 @@ public: // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters, const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, std::unordered_set* dimensionKeySet); virtual ConditionState getMetConditionDimension( const int index, const vector& dimensionFields, + const bool isSubOutputDimensionFields, std::unordered_set* dimensionsKeySet) const; + virtual const std::set* getChangedToTrueDimensions(const int index) const; + virtual const std::set* getChangedToFalseDimensions( + const int index) const; + bool equalOutputDimensions(const int index, const vector& dimensions); + + bool IsChangedDimensionTrackable(const int index); + bool IsSimpleCondition(const int index); + + ConditionState getUnSlicedPartConditionState(const int index) { + return mAllConditions[index]->getUnSlicedPartConditionState(); + } + void getTrueSlicedDimensions(const int index, + std::set* trueDimensions) const { + return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions); + } + private: std::vector> mAllConditions; }; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 624119f3ad045f40686dd302c22465bce1ed95f9..4913aef3347ff4e5dda0fe0b43cfe5ee98791f56 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -20,8 +20,6 @@ #include "SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" -#include - namespace android { namespace os { namespace statsd { @@ -36,7 +34,7 @@ SimpleConditionTracker::SimpleConditionTracker( const ConfigKey& key, const int64_t& id, const int index, const SimplePredicate& simplePredicate, const unordered_map& trackerNameIndexMap) - : ConditionTracker(id, index), mConfigKey(key) { + : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) { VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId); mCountNesting = simplePredicate.count_nesting(); @@ -82,6 +80,7 @@ SimpleConditionTracker::SimpleConditionTracker( mSliced = true; mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); } + mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions()); } if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { @@ -92,6 +91,10 @@ SimpleConditionTracker::SimpleConditionTracker( mNonSlicedConditionState = mInitialValue; + if (!mSliced) { + mUnSlicedPart = mInitialValue; + } + mInitialized = true; } @@ -108,10 +111,19 @@ bool SimpleConditionTracker::init(const vector& allConditionConfig, return mInitialized; } -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); +void SimpleConditionTracker::dumpState() { + VLOG("%lld DUMP:", (long long)mConditionId); + for (const auto& pair : mSlicedConditionState) { + VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second); + } + + VLOG("Changed to true keys: \n"); + for (const auto& key : mLastChangedToTrueDimensions) { + VLOG("%s", key.toString().c_str()); + } + VLOG("Changed to false keys: \n"); + for (const auto& key : mLastChangedToFalseDimensions) { + VLOG("%s", key.toString().c_str()); } } @@ -123,10 +135,19 @@ void SimpleConditionTracker::handleStopAll(std::vector& conditio (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false : true; + for (const auto& cond : mSlicedConditionState) { + if (cond.second > 0) { + mLastChangedToFalseDimensions.insert(cond.first); + } + } + // After StopAll, we know everything has stopped. From now on, default condition is false. mInitialValue = ConditionState::kFalse; mSlicedConditionState.clear(); conditionCache[mIndex] = ConditionState::kFalse; + if (!mSliced) { + mUnSlicedPart = ConditionState::kFalse; + } } bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -141,7 +162,7 @@ bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("Predicate %lld dropping data for dimension key %s", - (long long)mConditionId, newKey.c_str()); + (long long)mConditionId, newKey.toString().c_str()); return true; } } @@ -164,12 +185,14 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou // We get a new output key. newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse; if (matchStart && mInitialValue != ConditionState::kTrue) { - mSlicedConditionState.insert(std::make_pair(outputKey, 1)); + mSlicedConditionState[outputKey] = 1; changed = true; + mLastChangedToTrueDimensions.insert(outputKey); } else if (mInitialValue != ConditionState::kFalse) { // it's a stop and we don't have history about it. // If the default condition is not false, it means this stop is valuable to us. - mSlicedConditionState.insert(std::make_pair(outputKey, 0)); + mSlicedConditionState[outputKey] = 0; + mLastChangedToFalseDimensions.insert(outputKey); changed = true; } } else { @@ -179,6 +202,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse; if (matchStart) { if (startedCount == 0) { + mLastChangedToTrueDimensions.insert(outputKey); // This condition for this output key will change from false -> true changed = true; } @@ -202,6 +226,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou } // if everything has stopped for this output key, condition true -> false; if (startedCount == 0) { + mLastChangedToFalseDimensions.insert(outputKey); changed = true; } } @@ -209,18 +234,19 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou // if default condition is false, it means we don't need to keep the false values. if (mInitialValue == ConditionState::kFalse && startedCount == 0) { mSlicedConditionState.erase(outputIt); - VLOG("erase key %s", outputKey.c_str()); + VLOG("erase key %s", outputKey.toString().c_str()); } } } // dump all dimensions for debugging if (DEBUG) { - print(mSlicedConditionState, mConditionId); + dumpState(); } (*conditionChangedCache) = changed; (*conditionCache) = newCondition; + VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId, conditionChangedCache[mIndex] == true); } @@ -237,6 +263,8 @@ void SimpleConditionTracker::evaluateCondition( (long long)mConditionId, conditionCache[mIndex]); return; } + mLastChangedToTrueDimensions.clear(); + mLastChangedToFalseDimensions.clear(); if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) && eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) { @@ -275,6 +303,7 @@ void SimpleConditionTracker::evaluateCondition( conditionCache[mIndex] = itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; } + mUnSlicedPart = conditionCache[mIndex]; } return; @@ -286,6 +315,17 @@ void SimpleConditionTracker::evaluateCondition( if (mOutputDimensions.size() == 0) { handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState, &overallChanged); + } else if (!mContainANYPositionInInternalDimensions) { + HashableDimensionKey outputValue; + filterValues(mOutputDimensions, event.getValues(), &outputValue); + + // 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. + handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged); } else { std::vector outputValues; filterValues(mOutputDimensions, event.getValues(), &outputValues); @@ -297,23 +337,29 @@ void SimpleConditionTracker::evaluateCondition( // 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 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; + ConditionState tempState; bool tempChanged = false; handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged); if (tempChanged) { overallChanged = true; } + // ConditionState's | operator is overridden + overallState = overallState | tempState; } } conditionCache[mIndex] = overallState; conditionChangedCache[mIndex] = overallChanged; + if (!mSliced) { + mUnSlicedPart = overallState; + } } void SimpleConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector>& allConditions, - const vector& dimensionFields, vector& conditionCache, + const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, + vector& conditionCache, std::unordered_set& dimensionsKeySet) const { if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { @@ -328,7 +374,7 @@ void SimpleConditionTracker::isConditionMet( ConditionState conditionState = ConditionState::kNotEvaluated; if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) { conditionState = conditionState | getMetConditionDimension( - allConditions, dimensionFields, dimensionsKeySet); + allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet); } else { conditionState = conditionState | mInitialValue; if (!mSliced) { @@ -343,42 +389,48 @@ void SimpleConditionTracker::isConditionMet( conditionCache[mIndex] = conditionState; return; } - std::vector defaultKeys = { DEFAULT_DIMENSION_KEY }; - const std::vector &keys = - (pair == conditionParameters.end()) ? defaultKeys : pair->second; ConditionState conditionState = ConditionState::kNotEvaluated; - for (size_t i = 0; i < keys.size(); ++i) { - const HashableDimensionKey& key = keys[i]; + const HashableDimensionKey& key = pair->second; + if (isPartialLink) { + // For unseen key, check whether the require dimensions are subset of sliced condition + // output. + conditionState = conditionState | mInitialValue; + for (const auto& slice : mSlicedConditionState) { + ConditionState sliceState = + slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + if (slice.first.contains(key)) { + conditionState = conditionState | sliceState; + if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { + if (isSubOutputDimensionFields) { + HashableDimensionKey dimensionKey; + filterValues(dimensionFields, slice.first.getValues(), &dimensionKey); + dimensionsKeySet.insert(dimensionKey); + } else { + dimensionsKeySet.insert(slice.first); + } + } + } + } + } else { auto startedCountIt = mSlicedConditionState.find(key); + conditionState = conditionState | mInitialValue; if (startedCountIt != mSlicedConditionState.end()) { ConditionState sliceState = startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; conditionState = conditionState | sliceState; 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 - // output. - conditionState = conditionState | mInitialValue; - for (const auto& slice : mSlicedConditionState) { - ConditionState sliceState = - slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; - if (slice.first.contains(key)) { - conditionState = conditionState | sliceState; - if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { - vector dimensionKeys; - filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys); - - dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); - } - } + if (isSubOutputDimensionFields) { + HashableDimensionKey dimensionKey; + filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey); + dimensionsKeySet.insert(dimensionKey); + } else { + dimensionsKeySet.insert(startedCountIt->first); } } } + + } conditionCache[mIndex] = conditionState; VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); } @@ -386,6 +438,7 @@ void SimpleConditionTracker::isConditionMet( ConditionState SimpleConditionTracker::getMetConditionDimension( const std::vector>& allConditions, const vector& dimensionFields, + const bool isSubOutputDimensionFields, std::unordered_set& dimensionsKeySet) const { ConditionState conditionState = mInitialValue; if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 || @@ -405,10 +458,13 @@ ConditionState SimpleConditionTracker::getMetConditionDimension( conditionState = conditionState | sliceState; if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) { - vector dimensionKeys; - filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys); - - dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end()); + if (isSubOutputDimensionFields) { + HashableDimensionKey dimensionKey; + filterValues(dimensionFields, slice.first.getValues(), &dimensionKey); + dimensionsKeySet.insert(dimensionKey); + } else { + dimensionsKeySet.insert(slice.first); + } } } return conditionState; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index c565129352973ee4a35fb9bdeacdd0c306701255..47d1eceb90225dba0e80a7d994766e5bc74a6859 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -49,14 +49,55 @@ public: void isConditionMet(const ConditionKey& conditionParameters, const std::vector>& allConditions, const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, std::vector& conditionCache, std::unordered_set& dimensionsKeySet) const override; ConditionState getMetConditionDimension( const std::vector>& allConditions, const vector& dimensionFields, + const bool isSubOutputDimensionFields, std::unordered_set& dimensionsKeySet) const override; + virtual const std::set* getChangedToTrueDimensions( + const std::vector>& allConditions) const { + if (mSliced) { + return &mLastChangedToTrueDimensions; + } else { + return nullptr; + } + } + + virtual const std::set* getChangedToFalseDimensions( + const std::vector>& allConditions) const { + if (mSliced) { + return &mLastChangedToFalseDimensions; + } else { + return nullptr; + } + } + + void getTrueSlicedDimensions( + const std::vector>& allConditions, + std::set* dimensions) const override { + for (const auto& itr : mSlicedConditionState) { + if (itr.second > 0) { + dimensions->insert(itr.first); + } + } + } + + bool IsChangedDimensionTrackable() const override { return true; } + + bool IsSimpleCondition() const override { return true; } + + bool equalOutputDimensions( + const std::vector>& allConditions, + const vector& dimensions) const override { + return equalDimensions(mOutputDimensions, dimensions); + } + private: const ConfigKey mConfigKey; // The index of the LogEventMatcher which defines the start. @@ -75,6 +116,11 @@ private: std::vector mOutputDimensions; + bool mContainANYPositionInInternalDimensions; + + std::set mLastChangedToTrueDimensions; + std::set mLastChangedToFalseDimensions; + int mDimensionTag; std::map mSlicedConditionState; @@ -87,6 +133,8 @@ private: bool hitGuardRail(const HashableDimensionKey& newKey); + void dumpState(); + FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition); FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim); FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll); diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c68875c58162219f3aca51cf5f05edc8a217fdf2 --- /dev/null +++ b/cmds/statsd/src/condition/StateTracker.cpp @@ -0,0 +1,235 @@ +/* + * Copyright 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 "StateTracker.h" +#include "guardrail/StatsdStats.h" + +namespace android { +namespace os { +namespace statsd { + +using std::string; +using std::unordered_set; +using std::vector; + +StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index, + const SimplePredicate& simplePredicate, + const unordered_map& trackerNameIndexMap, + const vector primaryKeys) + : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) { + if (simplePredicate.has_start()) { + auto pair = trackerNameIndexMap.find(simplePredicate.start()); + if (pair == trackerNameIndexMap.end()) { + ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); + return; + } + mStartLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStartLogMatcherIndex); + } else { + ALOGW("Condition %lld must have a start matcher", (long long)id); + return; + } + + if (simplePredicate.has_dimensions()) { + translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); + if (mOutputDimensions.size() > 0) { + mSliced = true; + mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); + } else { + ALOGW("Condition %lld has invalid dimensions", (long long)id); + return; + } + } else { + ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id); + return; + } + + if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { + mInitialValue = ConditionState::kFalse; + } else { + mInitialValue = ConditionState::kUnknown; + } + + mNonSlicedConditionState = mInitialValue; + mInitialized = true; +} + +StateTracker::~StateTracker() { + VLOG("~StateTracker()"); +} + +bool StateTracker::init(const vector& allConditionConfig, + const vector>& allConditionTrackers, + const unordered_map& conditionIdIndexMap, + vector& stack) { + return mInitialized; +} + +void StateTracker::dumpState() { + VLOG("StateTracker %lld DUMP:", (long long)mConditionId); + for (const auto& value : mSlicedState) { + VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str()); + } + VLOG("Last Changed to True: "); + for (const auto& value : mLastChangedToTrueDimensions) { + VLOG("%s", value.toString().c_str()); + } + VLOG("Last Changed to False: "); + for (const auto& value : mLastChangedToFalseDimensions) { + VLOG("%s", value.toString().c_str()); + } +} + +bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) { + if (mSlicedState.find(newKey) != mSlicedState.end()) { + // if the condition is not sliced or the key is not new, we are good! + return false; + } + // 1. Report the tuple count if the tuple count > soft limit + if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mSlicedState.size() + 1; + StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("Predicate %lld dropping data for dimension key %s", + (long long)mConditionId, newKey.toString().c_str()); + return true; + } + } + return false; +} + +void StateTracker::evaluateCondition(const LogEvent& event, + const vector& eventMatcherValues, + const vector>& mAllConditions, + vector& conditionCache, + vector& conditionChangedCache) { + mLastChangedToTrueDimensions.clear(); + mLastChangedToFalseDimensions.clear(); + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + // it has been evaluated. + VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); + return; + } + + if (mStartLogMatcherIndex >= 0 && + eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) { + conditionCache[mIndex] = + mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionChangedCache[mIndex] = false; + return; + } + + VLOG("StateTracker evaluate event %s", event.ToString().c_str()); + + vector keys; + vector outputs; + filterValues(mPrimaryKeys, event.getValues(), &keys); + filterValues(mOutputDimensions, event.getValues(), &outputs); + if (keys.size() != 1 || outputs.size() != 1) { + ALOGE("More than 1 states in the event?? panic now!"); + conditionCache[mIndex] = + mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionChangedCache[mIndex] = false; + return; + } + // Primary key can exclusive fields must be simple fields. so there won't be more than + // one keys matched. + const auto& primaryKey = keys[0]; + const auto& state = outputs[0]; + hitGuardRail(primaryKey); + + VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str()); + + auto it = mSlicedState.find(primaryKey); + if (it == mSlicedState.end()) { + mSlicedState[primaryKey] = state; + conditionCache[mIndex] = ConditionState::kTrue; + mLastChangedToTrueDimensions.insert(state); + conditionChangedCache[mIndex] = true; + } else if (!(it->second == state)) { + mLastChangedToFalseDimensions.insert(it->second); + mLastChangedToTrueDimensions.insert(state); + mSlicedState[primaryKey] = state; + conditionCache[mIndex] = ConditionState::kTrue; + conditionChangedCache[mIndex] = true; + } else { + conditionCache[mIndex] = ConditionState::kTrue; + conditionChangedCache[mIndex] = false; + } + + if (DEBUG) { + dumpState(); + } + return; +} + +void StateTracker::isConditionMet( + const ConditionKey& conditionParameters, const vector>& allConditions, + const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, + vector& conditionCache, + std::unordered_set& dimensionsKeySet) const { + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + // it has been evaluated. + VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); + return; + } + + const auto pair = conditionParameters.find(mConditionId); + if (pair == conditionParameters.end()) { + if (mSlicedState.size() > 0) { + conditionCache[mIndex] = ConditionState::kTrue; + + for (const auto& state : mSlicedState) { + dimensionsKeySet.insert(state.second); + } + } else { + conditionCache[mIndex] = ConditionState::kUnknown; + } + return; + } + + const auto& primaryKey = pair->second; + conditionCache[mIndex] = mInitialValue; + auto it = mSlicedState.find(primaryKey); + if (it != mSlicedState.end()) { + conditionCache[mIndex] = ConditionState::kTrue; + dimensionsKeySet.insert(it->second); + } +} + +ConditionState StateTracker::getMetConditionDimension( + const std::vector>& allConditions, + const vector& dimensionFields, + const bool isSubOutputDimensionFields, + std::unordered_set& dimensionsKeySet) const { + if (mSlicedState.size() > 0) { + for (const auto& state : mSlicedState) { + dimensionsKeySet.insert(state.second); + } + return ConditionState::kTrue; + } + + return mInitialValue; +} + +} // namespace statsd +} // namespace os +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h new file mode 100644 index 0000000000000000000000000000000000000000..2bdf98c34c32980c28f0502f8980cbf580d55285 --- /dev/null +++ b/cmds/statsd/src/condition/StateTracker.h @@ -0,0 +1,131 @@ +/* + * Copyright 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 +#include "ConditionTracker.h" +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "stats_util.h" + +namespace android { +namespace os { +namespace statsd { + +class StateTracker : public virtual ConditionTracker { +public: + StateTracker(const ConfigKey& key, const int64_t& id, const int index, + const SimplePredicate& simplePredicate, + const std::unordered_map& trackerNameIndexMap, + const vector primaryKeys); + + ~StateTracker(); + + bool init(const std::vector& allConditionConfig, + const std::vector>& allConditionTrackers, + const std::unordered_map& conditionIdIndexMap, + std::vector& stack) override; + + void evaluateCondition(const LogEvent& event, + const std::vector& eventMatcherValues, + const std::vector>& mAllConditions, + std::vector& conditionCache, + std::vector& changedCache) override; + + /** + * Note: dimensionFields will be ignored in StateTracker, because we demand metrics + * must take the entire dimension fields from StateTracker. This is to make implementation + * simple and efficient. + * + * For example: wakelock duration by uid process states: + * dimension in condition must be {uid, process state}. + */ + void isConditionMet(const ConditionKey& conditionParameters, + const std::vector>& allConditions, + const vector& dimensionFields, + const bool isSubOutputDimensionFields, + const bool isPartialLink, + std::vector& conditionCache, + std::unordered_set& dimensionsKeySet) const override; + + /** + * Note: dimensionFields will be ignored in StateTracker, because we demand metrics + * must take the entire dimension fields from StateTracker. This is to make implementation + * simple and efficient. + */ + ConditionState getMetConditionDimension( + const std::vector>& allConditions, + const vector& dimensionFields, + const bool isSubOutputDimensionFields, + std::unordered_set& dimensionsKeySet) const override; + + virtual const std::set* getChangedToTrueDimensions( + const std::vector>& allConditions) const { + return &mLastChangedToTrueDimensions; + } + + virtual const std::set* getChangedToFalseDimensions( + const std::vector>& allConditions) const { + return &mLastChangedToFalseDimensions; + } + + bool IsChangedDimensionTrackable() const override { return true; } + + bool IsSimpleCondition() const override { return true; } + + bool equalOutputDimensions( + const std::vector>& allConditions, + const vector& dimensions) const override { + return equalDimensions(mOutputDimensions, dimensions); + } + + void getTrueSlicedDimensions( + const std::vector>& allConditions, + std::set* dimensions) const override { + for (const auto& itr : mSlicedState) { + dimensions->insert(itr.second); + } + } + +private: + const ConfigKey mConfigKey; + + // The index of the LogEventMatcher which defines the start. + int mStartLogMatcherIndex; + + std::set mLastChangedToTrueDimensions; + std::set mLastChangedToFalseDimensions; + + std::vector mOutputDimensions; + std::vector mPrimaryKeys; + + ConditionState mInitialValue; + + int mDimensionTag; + + void dumpState(); + + bool hitGuardRail(const HashableDimensionKey& newKey); + + // maps from [primary_key] to [primary_key, exclusive_state]. + std::unordered_map mSlicedState; + + FRIEND_TEST(StateTrackerTest, TestStateChange); +}; + +} // namespace statsd +} // namespace os +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp index d791f8632f3c0bb45ea97847d5e73aa89e4eefbd..4a2bd2799df81006f59685980b9210ae1a85eec2 100644 --- a/cmds/statsd/src/config/ConfigKey.cpp +++ b/cmds/statsd/src/config/ConfigKey.cpp @@ -16,14 +16,10 @@ #include "config/ConfigKey.h" -#include - namespace android { namespace os { namespace statsd { -using std::ostringstream; - ConfigKey::ConfigKey() { } @@ -37,9 +33,9 @@ ConfigKey::~ConfigKey() { } string ConfigKey::ToString() const { - ostringstream out; - out << '(' << mUid << ',' << mId << ')'; - return out.str(); + string s; + s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")"; + return s; } diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h index 3ad0eed3f2b92c37d041c63ba339ddc09893f051..dc79519d1f341ebfe0558940c88a36f0e9c74aa3 100644 --- a/cmds/statsd/src/config/ConfigKey.h +++ b/cmds/statsd/src/config/ConfigKey.h @@ -18,8 +18,6 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include -#include #include namespace android { @@ -27,7 +25,6 @@ namespace os { namespace statsd { using std::hash; -using std::ostream; using std::string; /** @@ -68,10 +65,6 @@ private: int mUid; }; -inline ostream& operator<<(ostream& os, const ConfigKey& config) { - return os << config.ToString(); -} - int64_t StrToInt64(const string& str); } // namespace statsd diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp deleted file mode 100644 index d1d9d3778267faefd7f5ab657cacccf6bcd2159c..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ /dev/null @@ -1,100 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include -#include "external/CpuTimePerUidFreqPuller.h" - -#include "../guardrail/StatsdStats.h" -#include "CpuTimePerUidFreqPuller.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "statslog.h" -#include "stats_log_util.h" - -using std::make_shared; -using std::shared_ptr; -using std::ifstream; - -namespace android { -namespace os { -namespace statsd { - -static const string sProcFile = "/proc/uid_time_in_state"; -static const int kLineBufferSize = 1024; - -/** - * Reads /proc/uid_time_in_state which has the format: - * - * uid: [freq1] [freq2] [freq3] ... - * [uid1]: [time in freq1] [time in freq2] [time in freq3] ... - * [uid2]: [time in freq1] [time in freq2] [time in freq3] ... - * ... - * - * This provides the times a UID's processes spent executing at each different cpu frequency. - * The file contains a monotonically increasing count of time for a single boot. - */ -CpuTimePerUidFreqPuller::CpuTimePerUidFreqPuller() - : StatsPuller(android::util::CPU_TIME_PER_UID_FREQ) { -} - -bool CpuTimePerUidFreqPuller::PullInternal(vector>* data) { - data->clear(); - - ifstream fin; - fin.open(sProcFile); - if (!fin.good()) { - VLOG("Failed to read pseudo file %s", sProcFile.c_str()); - return false; - } - - int64_t wallClockTimestampNs = getWallClockNs(); - int64_t elapsedTimestampNs = getElapsedRealtimeNs(); - - char buf[kLineBufferSize]; - // first line prints the format and frequencies - fin.getline(buf, kLineBufferSize); - char* pch; - while (!fin.eof()) { - fin.getline(buf, kLineBufferSize); - pch = strtok(buf, " :"); - if (pch == NULL) break; - uint64_t uid = std::stoull(pch); - pch = strtok(NULL, " "); - uint64_t timeMs; - int idx = 0; - do { - timeMs = std::stoull(pch); - auto ptr = make_shared(android::util::CPU_TIME_PER_UID_FREQ, - wallClockTimestampNs, elapsedTimestampNs); - ptr->write(uid); - ptr->write(idx); - ptr->write(timeMs); - ptr->init(); - data->push_back(ptr); - VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs); - idx++; - pch = strtok(NULL, " "); - } while (pch != NULL); - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h deleted file mode 100644 index 6f6c669fbea90e6863fa16e99961e4706fed6c88..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h +++ /dev/null @@ -1,42 +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 "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Reads /proc/uid_cputime/show_uid_stat which has the line format: - * - * uid: user_time_micro_seconds system_time_micro_seconds - * - * This provides the time a UID's processes spent executing in user-space and kernel-space. - * The file contains a monotonically increasing count of time for a single boot. - */ -class CpuTimePerUidFreqPuller : public StatsPuller { - public: - CpuTimePerUidFreqPuller(); - bool PullInternal(vector>* data) override; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp deleted file mode 100644 index 568b8f0c0c535171c391b2da04a732d15f5e95df..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp +++ /dev/null @@ -1,90 +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. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include -#include "external/CpuTimePerUidPuller.h" - -#include "CpuTimePerUidPuller.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "statslog.h" -#include "stats_log_util.h" - -using std::make_shared; -using std::shared_ptr; -using std::ifstream; - -namespace android { -namespace os { -namespace statsd { - -static const string sProcFile = "/proc/uid_cputime/show_uid_stat"; -static const int kLineBufferSize = 1024; - -/** - * Reads /proc/uid_cputime/show_uid_stat which has the line format: - * - * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds - * - * This provides the time a UID's processes spent executing in user-space and kernel-space. - * The file contains a monotonically increasing count of time for a single boot. - */ -CpuTimePerUidPuller::CpuTimePerUidPuller() : StatsPuller(android::util::CPU_TIME_PER_UID) { -} - -bool CpuTimePerUidPuller::PullInternal(vector>* data) { - data->clear(); - - ifstream fin; - fin.open(sProcFile); - if (!fin.good()) { - VLOG("Failed to read pseudo file %s", sProcFile.c_str()); - return false; - } - - int64_t wallClockTimestampNs = getWallClockNs(); - int64_t elapsedTimestampNs = getElapsedRealtimeNs(); - char buf[kLineBufferSize]; - char* pch; - while (!fin.eof()) { - fin.getline(buf, kLineBufferSize); - pch = strtok(buf, " :"); - if (pch == NULL) break; - uint64_t uid = std::stoull(pch); - pch = strtok(buf, " "); - uint64_t userTimeMs = std::stoull(pch); - pch = strtok(buf, " "); - uint64_t sysTimeMs = std::stoull(pch); - - auto ptr = make_shared(android::util::CPU_TIME_PER_UID, - wallClockTimestampNs, elapsedTimestampNs); - ptr->write(uid); - ptr->write(userTimeMs); - ptr->write(sysTimeMs); - ptr->init(); - data->push_back(ptr); - VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, - (long long)sysTimeMs); - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.h b/cmds/statsd/src/external/CpuTimePerUidPuller.h deleted file mode 100644 index d0d39d03aa5d0b5fe882353352605129d27bf1a8..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/external/CpuTimePerUidPuller.h +++ /dev/null @@ -1,42 +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 "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Reads /proc/uid_cputime/show_uid_stat which has the line format: - * - * uid: user_time_micro_seconds system_time_micro_seconds - * - * This provides the time a UID's processes spent executing in user-space and kernel-space. - * The file contains a monotonically increasing count of time for a single boot. - */ -class CpuTimePerUidPuller : public StatsPuller { - public: - CpuTimePerUidPuller(); - bool PullInternal(vector>* data) override; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp deleted file mode 100644 index 0b545ccb3658762b0e05f6b651150dcef4594d20..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 - -#include "KernelUidCpuActiveTimeReader.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "statslog.h" -#include "stats_log_util.h" - -using std::make_shared; -using std::shared_ptr; -using std::ifstream; - -namespace android { -namespace os { -namespace statsd { - -static const string sProcFile = "/proc/uid_concurrent_active_time"; -static const int kLineBufferSize = 1024; - -/** - * Reads /proc/uid_concurrent_active_time which has the format: - * active: X (X is # cores) - * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) - * [uid1]: [time-0] [time-1] [time-2] ... ... - * ... - * Time-N means the CPU time a UID spent running concurrently with N other processes. - * The file contains a monotonically increasing count of time for a single boot. - */ -KernelUidCpuActiveTimeReader::KernelUidCpuActiveTimeReader() : StatsPuller(android::util::CPU_ACTIVE_TIME) { -} - -bool KernelUidCpuActiveTimeReader::PullInternal(vector>* data) { - data->clear(); - - ifstream fin; - fin.open(sProcFile); - if (!fin.good()) { - VLOG("Failed to read pseudo file %s", sProcFile.c_str()); - return false; - } - - int64_t wallClockTimestampNs = getWallClockNs(); - int64_t elapsedTimestampNs = getElapsedRealtimeNs(); - - char buf[kLineBufferSize]; - char* pch; - while (!fin.eof()) { - fin.getline(buf, kLineBufferSize); - pch = strtok(buf, " :"); - if (pch == NULL) break; - uint64_t uid = std::stoull(pch); - pch = strtok(NULL, " "); - uint64_t timeMs; - int idx = 0; - do { - timeMs = std::stoull(pch); - auto ptr = make_shared(mTagId, wallClockTimestampNs, elapsedTimestampNs); - ptr->write(uid); - ptr->write(idx); - ptr->write(timeMs); - ptr->init(); - data->push_back(ptr); - VLOG("uid %lld, freq idx %d, active time %lld", (long long)uid, idx, (long long)timeMs); - idx++; - pch = strtok(NULL, " "); - } while (pch != NULL); - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp deleted file mode 100644 index cc80204b105ebe981ab577e21cce10430fa866c8..0000000000000000000000000000000000000000 --- a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 -#include "KernelUidCpuClusterTimeReader.h" -#include "guardrail/StatsdStats.h" -#include "logd/LogEvent.h" -#include "statslog.h" -#include "stats_log_util.h" - -using std::make_shared; -using std::shared_ptr; -using std::ifstream; - -namespace android { -namespace os { -namespace statsd { - -static const string sProcFile = "/proc/uid_concurrent_policy_time"; -static const int kLineBufferSize = 1024; - -/** - * Reads /proc/uid_concurrent_policy_time which has the format: - * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4) - * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... - * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... - * ... - * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes. - * The file contains a monotonically increasing count of time for a single boot. - */ -KernelUidCpuClusterTimeReader::KernelUidCpuClusterTimeReader() : StatsPuller(android::util::CPU_CLUSTER_TIME) { -} - -bool KernelUidCpuClusterTimeReader::PullInternal(vector>* data) { - data->clear(); - - ifstream fin; - fin.open(sProcFile); - if (!fin.good()) { - VLOG("Failed to read pseudo file %s", sProcFile.c_str()); - return false; - } - - int64_t wallClockTimestampNs = getWallClockNs(); - int64_t elapsedTimestampNs = getElapsedRealtimeNs(); - char buf[kLineBufferSize]; - char* pch; - while (!fin.eof()) { - fin.getline(buf, kLineBufferSize); - pch = strtok(buf, " :"); - if (pch == NULL) break; - uint64_t uid = std::stoull(pch); - pch = strtok(NULL, " "); - uint64_t timeMs; - int idx = 0; - do { - timeMs = std::stoull(pch); - auto ptr = make_shared(mTagId, wallClockTimestampNs, elapsedTimestampNs); - ptr->write(uid); - ptr->write(idx); - ptr->write(timeMs); - ptr->init(); - data->push_back(ptr); - VLOG("uid %lld, freq idx %d, cluster time %lld", (long long)uid, idx, (long long)timeMs); - idx++; - pch = strtok(NULL, " "); - } while (pch != NULL); - } - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h index e2e02533f17fd5830791abfc4596e055bff1c114..2a5679cc79fd34a5573e6284312ac19d3f76b2ce 100644 --- a/cmds/statsd/src/external/Perfetto.h +++ b/cmds/statsd/src/external/Perfetto.h @@ -16,6 +16,8 @@ #pragma once +#include + using android::os::StatsLogEventWrapper; namespace android { diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp index 261cb4332dd60604ae585dda8ff9718c12bbcbe2..3741202763b308e3f16f64ee281832b91623deca 100644 --- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp @@ -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,7 +14,7 @@ * limitations under the License. */ -#define DEBUG true // STOPSHIP if true +#define DEBUG false // STOPSHIP if true #include "Log.h" #include @@ -47,7 +47,6 @@ sp gHealthHal = nullptr; bool getHealthHal() { if (gHealthHal == nullptr) { gHealthHal = get_health_service(); - } return gHealthHal != nullptr; } diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3acdfcfce337be0314c25e1a25a45c96ff86c70 --- /dev/null +++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp @@ -0,0 +1,144 @@ +/* + * 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 +#include "external/ResourceThermalManagerPuller.h" +#include "external/StatsPuller.h" + +#include "ResourceThermalManagerPuller.h" +#include "logd/LogEvent.h" +#include "statslog.h" +#include "stats_log_util.h" + +#include + +using android::hardware::hidl_death_recipient; +using android::hardware::hidl_vec; +using android::hidl::base::V1_0::IBase; +using android::hardware::thermal::V1_0::IThermal; +using android::hardware::thermal::V1_0::Temperature; +using android::hardware::thermal::V1_0::ThermalStatus; +using android::hardware::thermal::V1_0::ThermalStatusCode; +using android::hardware::Return; +using android::hardware::Void; + +using std::chrono::duration_cast; +using std::chrono::nanoseconds; +using std::chrono::system_clock; +using std::make_shared; +using std::shared_ptr; + +namespace android { +namespace os { +namespace statsd { + +bool getThermalHalLocked(); +sp gThermalHal = nullptr; +std::mutex gThermalHalMutex; + +struct ThermalHalDeathRecipient : virtual public hidl_death_recipient { + virtual void serviceDied(uint64_t cookie, const wp& who) override { + std::lock_guard lock(gThermalHalMutex); + ALOGE("ThermalHAL just died"); + gThermalHal = nullptr; + getThermalHalLocked(); + } +}; + +sp gThermalHalDeathRecipient = nullptr; + +// The caller must be holding gThermalHalMutex. +bool getThermalHalLocked() { + if (gThermalHal == nullptr) { + gThermalHal = IThermal::getService(); + if (gThermalHal == nullptr) { + ALOGE("Unable to get Thermal service."); + } else { + if (gThermalHalDeathRecipient == nullptr) { + gThermalHalDeathRecipient = new ThermalHalDeathRecipient(); + } + hardware::Return linked = gThermalHal->linkToDeath( + gThermalHalDeathRecipient, 0x451F /* cookie */); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to ThermalHAL death: %s", + linked.description().c_str()); + gThermalHal = nullptr; + } else if (!linked) { + ALOGW("Unable to link to ThermalHal death notifications"); + gThermalHal = nullptr; + } else { + ALOGD("Link to death notification successful"); + } + } + } + return gThermalHal != nullptr; +} + +ResourceThermalManagerPuller::ResourceThermalManagerPuller() : + StatsPuller(android::util::TEMPERATURE) { +} + +bool ResourceThermalManagerPuller::PullInternal(vector>* data) { + std::lock_guard lock(gThermalHalMutex); + if (!getThermalHalLocked()) { + ALOGE("Thermal Hal not loaded"); + return false; + } + + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); + + data->clear(); + bool resultSuccess = true; + + Return ret = gThermalHal->getTemperatures( + [&](ThermalStatus status, const hidl_vec& temps) { + if (status.code != ThermalStatusCode::SUCCESS) { + ALOGE("Failed to get temperatures from ThermalHAL. Status: %d", status.code); + resultSuccess = false; + return; + } + if (mTagId == android::util::TEMPERATURE) { + for (size_t i = 0; i < temps.size(); ++i) { + auto ptr = make_shared(android::util::TEMPERATURE, + wallClockTimestampNs, elapsedTimestampNs); + ptr->write((static_cast(temps[i].type))); + ptr->write(temps[i].name); + ptr->write(temps[i].currentValue); + ptr->init(); + data->push_back(ptr); + } + } else { + ALOGE("Unsupported tag in ResourceThermalManagerPuller: %d", mTagId); + } + }); + if (!ret.isOk()) { + ALOGE("getThermalHalLocked() failed: thermal HAL service not available. Description: %s", + ret.description().c_str()); + if (ret.isDeadObject()) { + gThermalHal = nullptr; + } + return false; + } + return resultSuccess; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h b/cmds/statsd/src/external/ResourceThermalManagerPuller.h similarity index 84% rename from cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h rename to cmds/statsd/src/external/ResourceThermalManagerPuller.h index fcae35fa6eb5b35cb3d194f1addbc383fcf7d0e3..13c675aad680b7968c73b2b017f2de9e414247bd 100644 --- a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h +++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.h @@ -23,12 +23,15 @@ namespace android { namespace os { namespace statsd { -class KernelUidCpuActiveTimeReader : public StatsPuller { - public: - KernelUidCpuActiveTimeReader(); +/** + * Reads IThermal.hal + */ +class ResourceThermalManagerPuller : public StatsPuller { +public: + ResourceThermalManagerPuller(); bool PullInternal(vector>* data) override; }; } // namespace statsd } // namespace os -} // namespace android +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index bee99396126d4c097c789eda27bca20480ebd44a..72a00cb6834e0e80a6e71086bb011759de83dbd0 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -21,12 +21,8 @@ #include #include #include -#include "CpuTimePerUidFreqPuller.h" -#include "CpuTimePerUidPuller.h" #include "ResourceHealthManagerPuller.h" -#include "KernelUidCpuActiveTimeReader.h" -#include "KernelUidCpuClusterTimeReader.h" -#include "SubsystemSleepStatePuller.h" +#include "ResourceThermalManagerPuller.h" #include "StatsCompanionServicePuller.h" #include "StatsPullerManagerImpl.h" #include "StatsService.h" @@ -51,27 +47,19 @@ namespace statsd { const std::map StatsPullerManagerImpl::kAllPullAtomInfo = { // wifi_bytes_transfer {android::util::WIFI_BYTES_TRANSFER, - {{2, 3, 4, 5}, - {}, - 1, + {{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, + {{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, + {{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, + {{3, 4, 5, 6}, {2}, 1, new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}}, // bluetooth_bytes_transfer {android::util::BLUETOOTH_BYTES_TRANSFER, @@ -80,14 +68,26 @@ const std::map StatsPullerManagerImpl::kAllPullAtomInfo = { {android::util::KERNEL_WAKELOCK, {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}}, // subsystem_sleep_state - {android::util::SUBSYSTEM_SLEEP_STATE, {{}, {}, 1, new SubsystemSleepStatePuller()}}, + {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()}}, + {android::util::CPU_TIME_PER_UID, + {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}}, // cpu_time_per_uid_freq - {android::util::CPU_TIME_PER_UID_FREQ, {{3}, {2}, 1, new CpuTimePerUidFreqPuller()}}, + // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader + {android::util::CPU_TIME_PER_UID_FREQ, + {{4}, {2,3}, 0, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}}, + // cpu_active_time + // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader + {android::util::CPU_ACTIVE_TIME, + {{2}, {}, 0, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}}, + // cpu_cluster_time + // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader + {android::util::CPU_CLUSTER_TIME, + {{3}, {2}, 0, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}}, // wifi_activity_energy_info {android::util::WIFI_ACTIVITY_ENERGY_INFO, {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}}, @@ -103,10 +103,6 @@ const std::map StatsPullerManagerImpl::kAllPullAtomInfo = { // 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)}}, @@ -118,7 +114,12 @@ const std::map StatsPullerManagerImpl::kAllPullAtomInfo = { {{}, {}, 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)}}}; + {{4,5,6,7,8}, + {2,3}, + 0, + new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}, + // temperature + {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}}; StatsPullerManagerImpl::StatsPullerManagerImpl() : mCurrentPullingInterval(LONG_MAX) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 66cb1d04a4e1852a879e74dd852c7d2558d35be2..d626d901cdd09891254b630670ab7a4f5fb33287 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -36,6 +36,7 @@ using android::util::FIELD_TYPE_STRING; using android::util::ProtoOutputStream; using std::lock_guard; using std::map; +using std::shared_ptr; using std::string; using std::vector; @@ -47,15 +48,49 @@ const int FIELD_ID_UIDMAP_STATS = 8; const int FIELD_ID_ANOMALY_ALARM_STATS = 9; // 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_SUBSCRIBER_ALARM_STATS = 12; const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1; +const int FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED = 1; const int FIELD_ID_LOGGER_STATS_TIME = 1; const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2; +const int FIELD_ID_CONFIG_STATS_UID = 1; +const int FIELD_ID_CONFIG_STATS_ID = 2; +const int FIELD_ID_CONFIG_STATS_CREATION = 3; +const int FIELD_ID_CONFIG_STATS_DELETION = 4; +const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5; +const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6; +const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7; +const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8; +const int FIELD_ID_CONFIG_STATS_VALID = 9; +const int FIELD_ID_CONFIG_STATS_BROADCAST = 10; +const int FIELD_ID_CONFIG_STATS_DATA_DROP = 11; +const int FIELD_ID_CONFIG_STATS_DUMP_REPORT = 12; +const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13; +const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14; +const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; +const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; + +const int FIELD_ID_MATCHER_STATS_ID = 1; +const int FIELD_ID_MATCHER_STATS_COUNT = 2; +const int FIELD_ID_CONDITION_STATS_ID = 1; +const int FIELD_ID_CONDITION_STATS_COUNT = 2; +const int FIELD_ID_METRIC_STATS_ID = 1; +const int FIELD_ID_METRIC_STATS_COUNT = 2; +const int FIELD_ID_ALERT_STATS_ID = 1; +const int FIELD_ID_ALERT_STATS_COUNT = 2; + +const int FIELD_ID_UID_MAP_SNAPSHOTS = 1; +const int FIELD_ID_UID_MAP_CHANGES = 2; +const int FIELD_ID_UID_MAP_BYTES_USED = 3; +const int FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS = 4; +const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 5; + std::map StatsdStats::kPullerCooldownMap = { {android::util::KERNEL_WAKELOCK, 1}, {android::util::WIFI_BYTES_TRANSFER, 1}, @@ -79,7 +114,7 @@ StatsdStats& StatsdStats::getInstance() { return statsInstance; } -void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) { +void StatsdStats::addToIceBoxLocked(shared_ptr& stats) { // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize. if (mIceBox.size() == kMaxIceBoxSize) { mIceBox.pop_front(); @@ -95,20 +130,20 @@ void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int // If there is an existing config for the same key, icebox the old config. noteConfigRemovedInternalLocked(key); - StatsdStatsReport_ConfigStats configStats; - configStats.set_uid(key.GetUid()); - configStats.set_id(key.GetId()); - configStats.set_creation_time_sec(nowTimeSec); - configStats.set_metric_count(metricsCount); - configStats.set_condition_count(conditionsCount); - configStats.set_matcher_count(matchersCount); - configStats.set_alert_count(alertsCount); - configStats.set_is_valid(isValid); + shared_ptr configStats = std::make_shared(); + configStats->uid = key.GetUid(); + configStats->id = key.GetId(); + configStats->creation_time_sec = nowTimeSec; + configStats->metric_count = metricsCount; + configStats->condition_count = conditionsCount; + configStats->matcher_count = matchersCount; + configStats->alert_count = alertsCount; + configStats->is_valid = isValid; if (isValid) { mConfigStats[key] = configStats; } else { - configStats.set_deletion_time_sec(nowTimeSec); + configStats->deletion_time_sec = nowTimeSec; addToIceBoxLocked(configStats); } } @@ -117,14 +152,7 @@ void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { auto it = mConfigStats.find(key); if (it != mConfigStats.end()) { int32_t nowTimeSec = getWallClockSec(); - it->second.set_deletion_time_sec(nowTimeSec); - // Add condition stats, metrics stats, matcher stats, alert stats - addSubStatsToConfigLocked(key, it->second); - // Remove them after they are added to the config stats. - mMatcherStats.erase(key); - mMetricsStats.erase(key); - mAlertStats.erase(key); - mConditionStats.erase(key); + it->second->deletion_time_sec = nowTimeSec; addToIceBoxLocked(it->second); mConfigStats.erase(it); } @@ -146,12 +174,10 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { ALOGE("Config key %s not found!", key.ToString().c_str()); return; } - if (it->second.broadcast_sent_time_sec_size() >= kMaxTimestampCount) { - auto timestampList = it->second.mutable_broadcast_sent_time_sec(); - // This is O(N) operation. It shouldn't happen often, and N is only 20. - timestampList->erase(timestampList->begin()); + if (it->second->broadcast_sent_time_sec.size() == kMaxTimestampCount) { + it->second->broadcast_sent_time_sec.pop_front(); } - it->second.add_broadcast_sent_time_sec(timeSec); + it->second->broadcast_sent_time_sec.push_back(timeSec); } void StatsdStats::noteDataDropped(const ConfigKey& key) { @@ -165,12 +191,10 @@ void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) { ALOGE("Config key %s not found!", key.ToString().c_str()); return; } - if (it->second.data_drop_time_sec_size() >= kMaxTimestampCount) { - auto timestampList = it->second.mutable_data_drop_time_sec(); - // This is O(N) operation. It shouldn't happen often, and N is only 20. - timestampList->erase(timestampList->begin()); + if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) { + it->second->data_drop_time_sec.pop_front(); } - it->second.add_data_drop_time_sec(timeSec); + it->second->data_drop_time_sec.push_back(timeSec); } void StatsdStats::noteMetricsReportSent(const ConfigKey& key) { @@ -184,39 +208,42 @@ void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) { ALOGE("Config key %s not found!", key.ToString().c_str()); return; } - if (it->second.dump_report_time_sec_size() >= kMaxTimestampCount) { - auto timestampList = it->second.mutable_dump_report_time_sec(); - // This is O(N) operation. It shouldn't happen often, and N is only 20. - timestampList->erase(timestampList->begin()); + if (it->second->dump_report_time_sec.size() == kMaxTimestampCount) { + it->second->dump_report_time_sec.pop_front(); } - it->second.add_dump_report_time_sec(timeSec); + it->second->dump_report_time_sec.push_back(timeSec); } void StatsdStats::noteUidMapDropped(int snapshots, int deltas) { lock_guard lock(mLock); - mUidMapStats.set_dropped_snapshots(mUidMapStats.dropped_snapshots() + snapshots); - mUidMapStats.set_dropped_changes(mUidMapStats.dropped_changes() + deltas); + mUidMapStats.dropped_snapshots += mUidMapStats.dropped_snapshots + snapshots; + mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas; } void StatsdStats::setUidMapSnapshots(int snapshots) { lock_guard lock(mLock); - mUidMapStats.set_snapshots(snapshots); + mUidMapStats.snapshots = snapshots; } void StatsdStats::setUidMapChanges(int changes) { lock_guard lock(mLock); - mUidMapStats.set_changes(changes); + mUidMapStats.changes = changes; } void StatsdStats::setCurrentUidMapMemory(int bytes) { lock_guard lock(mLock); - mUidMapStats.set_bytes_used(bytes); + mUidMapStats.bytes_used = bytes; } void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) { lock_guard lock(mLock); // if name doesn't exist before, it will create the key with count 0. - auto& conditionSizeMap = mConditionStats[key]; + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + + auto& conditionSizeMap = statsIt->second->condition_stats; if (size > conditionSizeMap[id]) { conditionSizeMap[id] = size; } @@ -225,7 +252,11 @@ void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) { lock_guard lock(mLock); // if name doesn't exist before, it will create the key with count 0. - auto& metricsDimensionMap = mMetricsStats[key]; + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + auto& metricsDimensionMap = statsIt->second->metric_stats; if (size > metricsDimensionMap[id]) { metricsDimensionMap[id] = size; } @@ -233,14 +264,21 @@ void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& i void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) { lock_guard lock(mLock); - auto& matcherStats = mMatcherStats[key]; - matcherStats[id]++; + + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + statsIt->second->matcher_stats[id]++; } void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) { lock_guard lock(mLock); - auto& alertStats = mAlertStats[key]; - alertStats[id]++; + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + statsIt->second->alert_stats[id]++; } void StatsdStats::noteRegisteredAnomalyAlarmChanged() { @@ -248,6 +286,11 @@ void StatsdStats::noteRegisteredAnomalyAlarmChanged() { mAnomalyAlarmRegisteredStats++; } +void StatsdStats::noteRegisteredPeriodicAlarmChanged() { + lock_guard lock(mLock); + mPeriodicAlarmRegisteredStats++; +} + void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) { lock_guard lock(mLock); mPulledAtomStats[pullAtomId].minPullIntervalSec = intervalSec; @@ -292,68 +335,18 @@ void StatsdStats::resetInternalLocked() { // Reset the historical data, but keep the active ConfigStats mStartTimeSec = getWallClockSec(); mIceBox.clear(); - mConditionStats.clear(); - mMetricsStats.clear(); std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); - mAlertStats.clear(); mAnomalyAlarmRegisteredStats = 0; - mMatcherStats.clear(); + mPeriodicAlarmRegisteredStats = 0; mLoggerErrors.clear(); for (auto& config : mConfigStats) { - config.second.clear_broadcast_sent_time_sec(); - config.second.clear_data_drop_time_sec(); - config.second.clear_dump_report_time_sec(); - config.second.clear_matcher_stats(); - config.second.clear_condition_stats(); - config.second.clear_metric_stats(); - config.second.clear_alert_stats(); - } -} - -void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, - StatsdStatsReport_ConfigStats& configStats) { - // Add matcher stats - if (mMatcherStats.find(key) != mMatcherStats.end()) { - const auto& matcherStats = mMatcherStats[key]; - for (const auto& stats : matcherStats) { - auto output = configStats.add_matcher_stats(); - output->set_id(stats.first); - output->set_matched_times(stats.second); - VLOG("matcher %lld matched %d times", - (long long)stats.first, stats.second); - } - } - // Add condition stats - if (mConditionStats.find(key) != mConditionStats.end()) { - const auto& conditionStats = mConditionStats[key]; - for (const auto& stats : conditionStats) { - auto output = configStats.add_condition_stats(); - output->set_id(stats.first); - output->set_max_tuple_counts(stats.second); - VLOG("condition %lld max output tuple size %d", - (long long)stats.first, stats.second); - } - } - // Add metrics stats - if (mMetricsStats.find(key) != mMetricsStats.end()) { - const auto& conditionStats = mMetricsStats[key]; - for (const auto& stats : conditionStats) { - auto output = configStats.add_metric_stats(); - output->set_id(stats.first); - output->set_max_tuple_counts(stats.second); - VLOG("metrics %lld max output tuple size %d", - (long long)stats.first, stats.second); - } - } - // Add anomaly detection alert stats - if (mAlertStats.find(key) != mAlertStats.end()) { - const auto& alertStats = mAlertStats[key]; - for (const auto& stats : alertStats) { - auto output = configStats.add_alert_stats(); - output->set_id(stats.first); - output->set_alerted_times(stats.second); - VLOG("alert %lld declared %d times", (long long)stats.first, stats.second); - } + config.second->broadcast_sent_time_sec.clear(); + config.second->data_drop_time_sec.clear(); + config.second->dump_report_time_sec.clear(); + config.second->matcher_stats.clear(); + config.second->condition_stats.clear(); + config.second->metric_stats.clear(); + config.second->alert_stats.clear(); } } @@ -367,80 +360,59 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size()); for (const auto& configStats : mIceBox) { fprintf(out, - "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + "Config {%d_%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, valid=%d\n", - configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), - configStats.deletion_time_sec(), configStats.metric_count(), - configStats.condition_count(), configStats.matcher_count(), - configStats.alert_count(), configStats.is_valid()); + configStats->uid, (long long)configStats->id, configStats->creation_time_sec, + configStats->deletion_time_sec, configStats->metric_count, + configStats->condition_count, configStats->matcher_count, configStats->alert_count, + configStats->is_valid); - for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { fprintf(out, "\tbroadcast time: %d\n", broadcastTime); } - for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + for (const auto& dataDropTime : configStats->data_drop_time_sec) { fprintf(out, "\tdata drop time: %d\n", dataDropTime); } } fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); for (auto& pair : mConfigStats) { - auto& key = pair.first; auto& configStats = pair.second; - fprintf(out, "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, valid=%d\n", - configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), - configStats.deletion_time_sec(), configStats.metric_count(), - configStats.condition_count(), configStats.matcher_count(), - configStats.alert_count(), configStats.is_valid()); - for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + configStats->uid, (long long)configStats->id, configStats->creation_time_sec, + configStats->deletion_time_sec, configStats->metric_count, + configStats->condition_count, configStats->matcher_count, configStats->alert_count, + configStats->is_valid); + for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { fprintf(out, "\tbroadcast time: %d\n", broadcastTime); } - for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + for (const auto& dataDropTime : configStats->data_drop_time_sec) { fprintf(out, "\tdata drop time: %d\n", dataDropTime); } - for (const auto& dumpTime : configStats.dump_report_time_sec()) { + for (const auto& dumpTime : configStats->dump_report_time_sec) { fprintf(out, "\tdump report time: %d\n", dumpTime); } - // Add matcher stats - auto matcherIt = mMatcherStats.find(key); - if (matcherIt != mMatcherStats.end()) { - const auto& matcherStats = matcherIt->second; - for (const auto& stats : matcherStats) { - fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, - stats.second); - } + for (const auto& stats : pair.second->matcher_stats) { + fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second); } - // Add condition stats - auto conditionIt = mConditionStats.find(key); - if (conditionIt != mConditionStats.end()) { - const auto& conditionStats = conditionIt->second; - for (const auto& stats : conditionStats) { - fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } + + for (const auto& stats : pair.second->condition_stats) { + fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, + stats.second); } - // Add metrics stats - auto metricIt = mMetricsStats.find(key); - if (metricIt != mMetricsStats.end()) { - const auto& conditionStats = metricIt->second; - for (const auto& stats : conditionStats) { - fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } + + for (const auto& stats : pair.second->condition_stats) { + fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, + stats.second); } - // Add anomaly detection alert stats - auto alertIt = mAlertStats.find(key); - if (alertIt != mAlertStats.end()) { - const auto& alertStats = alertIt->second; - for (const auto& stats : alertStats) { - fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, - stats.second); - } + + for (const auto& stats : pair.second->alert_stats) { + fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second); } } fprintf(out, "********Pushed Atom stats***********\n"); @@ -454,7 +426,7 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "********Pulled Atom stats***********\n"); for (const auto& pair : mPulledAtomStats) { fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull, - (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec); + (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec); } if (mAnomalyAlarmRegisteredStats > 0) { @@ -462,11 +434,16 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); } + if (mPeriodicAlarmRegisteredStats > 0) { + fprintf(out, "********SubscriberAlarmStats stats***********\n"); + fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats); + } + fprintf(out, "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes " "lost=%d\n", - mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(), - mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes()); + mUidMapStats.bytes_used, mUidMapStats.snapshots, mUidMapStats.changes, + mUidMapStats.dropped_snapshots, mUidMapStats.dropped_changes); for (const auto& error : mLoggerErrors) { time_t error_time = error.first; @@ -477,6 +454,73 @@ void StatsdStats::dumpStats(FILE* out) const { } } +void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) { + uint64_t token = + proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_UID, configStats.uid); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ID, (long long)configStats.id); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CREATION, configStats.creation_time_sec); + if (configStats.deletion_time_sec != 0) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DELETION, + configStats.deletion_time_sec); + } + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_METRIC_COUNT, configStats.metric_count); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CONDITION_COUNT, + configStats.condition_count); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_MATCHER_COUNT, configStats.matcher_count); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count); + proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid); + + for (const auto& broadcast : configStats.broadcast_sent_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED, + broadcast); + } + + for (const auto& drop : configStats.data_drop_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP | FIELD_COUNT_REPEATED, + drop); + } + + for (const auto& dump : configStats.dump_report_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT | FIELD_COUNT_REPEATED, + dump); + } + + for (const auto& pair : configStats.matcher_stats) { + uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_MATCHER_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_MATCHER_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_MATCHER_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + for (const auto& pair : configStats.condition_stats) { + uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_CONDITION_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONDITION_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + for (const auto& pair : configStats.metric_stats) { + uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_METRIC_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + for (const auto& pair : configStats.alert_stats) { + uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_ALERT_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_ALERT_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + proto->end(token); +} + void StatsdStats::dumpStats(std::vector* output, bool reset) { lock_guard lock(mLock); @@ -485,34 +529,17 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec()); for (const auto& configStats : mIceBox) { - const int numBytes = configStats.ByteSize(); - vector buffer(numBytes); - configStats.SerializeToArray(&buffer[0], numBytes); - proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], - buffer.size()); + addConfigStatsToProto(*configStats, &proto); } for (auto& pair : mConfigStats) { - auto& configStats = pair.second; - addSubStatsToConfigLocked(pair.first, configStats); - - const int numBytes = configStats.ByteSize(); - vector buffer(numBytes); - configStats.SerializeToArray(&buffer[0], numBytes); - proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], - buffer.size()); - // reset the sub stats, the source of truth is in the individual map - // they will be repopulated when dumpStats() is called again. - configStats.clear_matcher_stats(); - configStats.clear_condition_stats(); - configStats.clear_metric_stats(); - configStats.clear_alert_stats(); + addConfigStatsToProto(*(pair.second), &proto); } const size_t atomCounts = mPushedAtomStats.size(); for (size_t i = 2; i < atomCounts; i++) { if (mPushedAtomStats[i] > 0) { - long long token = + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]); @@ -525,19 +552,30 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { } if (mAnomalyAlarmRegisteredStats > 0) { - long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED, mAnomalyAlarmRegisteredStats); proto.end(token); } - const int numBytes = mUidMapStats.ByteSize(); - vector buffer(numBytes); - mUidMapStats.SerializeToArray(&buffer[0], numBytes); - proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size()); + if (mPeriodicAlarmRegisteredStats > 0) { + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SUBSCRIBER_ALARM_STATS); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED, + mPeriodicAlarmRegisteredStats); + proto.end(token); + } + + uint64_t uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_SNAPSHOTS, mUidMapStats.snapshots); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS, + mUidMapStats.dropped_snapshots); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes); + proto.end(uidMapToken); for (const auto& error : mLoggerErrors) { - long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS | + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS | FIELD_COUNT_REPEATED); proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_TIME, error.first); proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_ERROR_CODE, error.second); @@ -566,4 +604,4 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 8c16e4e9c2eb64fbf9400ca8b89a2becc7d9be4f..c3f401324454c89c5afae6b54359cde0edf8058d 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -30,8 +30,49 @@ namespace android { namespace os { namespace statsd { +struct ConfigStats { + int32_t uid; + int64_t id; + int32_t creation_time_sec; + int32_t deletion_time_sec = 0; + int32_t metric_count; + int32_t condition_count; + int32_t matcher_count; + int32_t alert_count; + bool is_valid; + + std::list broadcast_sent_time_sec; + std::list data_drop_time_sec; + std::list dump_report_time_sec; + + // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount. + std::map matcher_stats; + + // Stores the number of output tuple of condition trackers when it's bigger than + // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, + // it means some data has been dropped. The map size is capped by kMaxConfigCount. + std::map condition_stats; + + // Stores the number of output tuple of metric producers when it's bigger than + // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, + // it means some data has been dropped. The map size is capped by kMaxConfigCount. + std::map metric_stats; + + // Stores the number of times an anomaly detection alert has been declared. + // The map size is capped by kMaxConfigCount. + std::map alert_stats; +}; + +struct UidMapStats { + int32_t snapshots; + int32_t changes; + int32_t bytes_used; + int32_t dropped_snapshots; + int32_t dropped_changes; +}; + // Keeps track of stats of statsd. -// Single instance shared across the process. All methods are thread safe. +// Single instance shared across the process. All public methods are thread safe. class StatsdStats { public: static StatsdStats& getInstance(); @@ -169,6 +210,11 @@ public: */ void noteRegisteredAnomalyAlarmChanged(); + /** + * Report that statsd modified the periodic alarm registered with StatsCompanionService. + */ + void noteRegisteredPeriodicAlarmChanged(); + /** * Records the number of snapshot and delta entries that are being dropped from the uid map. */ @@ -228,25 +274,15 @@ private: int32_t mStartTimeSec; // Track the number of dropped entries used by the uid map. - StatsdStatsReport_UidMapStats mUidMapStats; + UidMapStats mUidMapStats; // The stats about the configs that are still in use. // The map size is capped by kMaxConfigCount. - std::map mConfigStats; + std::map> mConfigStats; // Stores the stats for the configs that are no longer in use. // The size of the vector is capped by kMaxIceBoxSize. - std::list mIceBox; - - // Stores the number of output tuple of condition trackers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map> mConditionStats; - - // Stores the number of output tuple of metric producers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map> mMetricsStats; + std::list> mIceBox; // Stores the number of times a pushed atom is logged. // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms @@ -264,27 +300,21 @@ private: // StatsCompanionService. int mAnomalyAlarmRegisteredStats = 0; - // Stores the number of times an anomaly detection alert has been declared - // (per config, per alert name). The map size is capped by kMaxConfigCount. - std::map> mAlertStats; + // Stores the number of times statsd registers the periodic alarm changes + int mPeriodicAlarmRegisteredStats = 0; - // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount. - std::map> mMatcherStats; void noteConfigRemovedInternalLocked(const ConfigKey& key); void resetInternalLocked(); - void addSubStatsToConfigLocked(const ConfigKey& key, - StatsdStatsReport_ConfigStats& configStats); - void noteDataDropped(const ConfigKey& key, int32_t timeSec); void noteMetricsReportSent(const ConfigKey& key, int32_t timeSec); void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); - void addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats); + void addToIceBoxLocked(std::shared_ptr& stats); FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index d282b86f52bdf7a9459a734fae567ab1b32f600c..4e4f146d27ac8582671157ecf91aba6d3c604e42 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -17,8 +17,6 @@ #define DEBUG false // STOPSHIP if true #include "logd/LogEvent.h" -#include - #include "stats_log_util.h" namespace android { @@ -27,7 +25,6 @@ namespace statsd { using namespace android::util; using android::util::ProtoOutputStream; -using std::ostringstream; using std::string; using std::vector; @@ -381,16 +378,15 @@ float LogEvent::GetFloat(size_t key, status_t* err) const { } string LogEvent::ToString() const { - ostringstream result; - result << "{ " << mLogdTimestampNs << " " << mElapsedTimestampNs << " (" << mTagId << ")"; + string result; + result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs, + (long long)mElapsedTimestampNs, mTagId); for (const auto& value : mValues) { - result << StringPrintf("%#x", value.mField.getField()); - result << "->"; - result << value.mValue.toString(); - result << " "; + result += + StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " "; } - result << " }"; - return result.str(); + result += " }"; + return result; } void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 461200905a2590ba39c8d1a5dc5a2ec1c7837618..364d4e9d5c9443c88fe021da337350c3ff3e6e4e 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -21,7 +21,6 @@ #include "matchers/matcher_util.h" #include "stats_util.h" -using std::ostringstream; using std::set; using std::string; using std::unordered_map; @@ -87,6 +86,10 @@ bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value const string& str_match) { if (isAttributionUidField(field, value)) { int uid = value.int_value; + auto aidIt = UidMap::sAidToUidMapping.find(str_match); + if (aidIt != UidMap::sAidToUidMapping.end()) { + return ((int)aidIt->second) == uid; + } std::set packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/); return packageNames.find(str_match) != packageNames.end(); } else if (value.getType() == STRING) { @@ -207,6 +210,9 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } return false; } + // Finally, we get to the point of real value matching. + // If the field matcher ends with ANY, then we have [start, end) range > 1. + // In the following, we should return true, when ANY of the values matches. case FieldValueMatcher::ValueMatcherCase::kEqBool: { for (int i = start; i < end; i++) { if ((values[i].mValue.getType() == INT && @@ -225,9 +231,36 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, return true; } } + return false; } + case FieldValueMatcher::ValueMatcherCase::kNeqAllString: { + const auto& str_list = matcher.neq_all_string(); + for (int i = start; i < end; i++) { + bool notEqAll = true; + for (const auto& str : str_list.str_value()) { + if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) { + notEqAll = false; + break; + } + } + if (notEqAll) { + return true; + } + } return false; - case FieldValueMatcher::ValueMatcherCase::kEqInt: + } + case FieldValueMatcher::ValueMatcherCase::kEqAnyString: { + const auto& str_list = matcher.eq_any_string(); + for (int i = start; i < end; i++) { + for (const auto& str : str_list.str_value()) { + if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) { + return true; + } + } + } + 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)) { @@ -240,7 +273,8 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; - case FieldValueMatcher::ValueMatcherCase::kLtInt: + } + 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())) { @@ -253,7 +287,8 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; - case FieldValueMatcher::ValueMatcherCase::kGtInt: + } + 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())) { @@ -266,7 +301,8 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; - case FieldValueMatcher::ValueMatcherCase::kLtFloat: + } + 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())) { @@ -274,7 +310,8 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; - case FieldValueMatcher::ValueMatcherCase::kGtFloat: + } + 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())) { @@ -282,7 +319,8 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; - case FieldValueMatcher::ValueMatcherCase::kLteInt: + } + 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())) { @@ -295,7 +333,8 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; - case FieldValueMatcher::ValueMatcherCase::kGteInt: + } + 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())) { @@ -308,6 +347,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } } return false; + } default: return false; } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index af2e362368c331e4669521f93c11b9e7995cff65..22b2a30af328acb478226917e020b55c0d5750e0 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -71,6 +71,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric if (metric.has_dimensions_in_what()) { translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); } if (metric.has_dimensions_in_condition()) { @@ -109,23 +110,23 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, return; } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); + uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); for (const auto& counter : mPastBuckets) { const MetricDimensionKey& dimensionKey = counter.first; - VLOG(" dimension key %s", dimensionKey.c_str()); + VLOG(" dimension key %s", dimensionKey.toString().c_str()); - long long wrapperToken = + uint64_t wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. - long long dimensionInWhatToken = protoOutput->start( + uint64_t dimensionInWhatToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionInWhatToken); if (dimensionKey.hasDimensionKeyInCondition()) { - long long dimensionInConditionToken = protoOutput->start( + uint64_t dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); @@ -134,7 +135,7 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // Then fill bucket_info (CountBucketInfo). for (const auto& bucket : counter.second) { - long long bucketInfoToken = protoOutput->start( + uint64_t bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); @@ -151,8 +152,11 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(protoToken); mPastBuckets.clear(); +} - // TODO: Clear mDimensionKeyMap once the report is dumped. +void CountMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); } void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, @@ -173,7 +177,7 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("CountMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.c_str()); + (long long)mMetricId, newKey.toString().c_str()); return true; } } @@ -215,7 +219,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( countWholeBucket); } - VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(), + VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.toString().c_str(), (long long)(*mCurrentSlicedCounter)[eventKey]); } @@ -250,7 +254,8 @@ void CountMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) 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(), + VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId, + counter.first.toString().c_str(), (long long)counter.second); } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 0c4291d5dfc2abf229b915cb49cfdc8c81cbdc1b..1d8e42be635cb1993862d3a8ce76cc85f3a62d3e 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -69,6 +69,8 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& newEventTime) override; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 9c65371ba958b08c7244aa941ff53e98da3659af..bc09683d79ade0466033a50e1d4b84d2cc8eab3b 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -67,7 +67,8 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat mStartIndex(startIndex), mStopIndex(stopIndex), mStopAllIndex(stopAllIndex), - mNested(nesting) { + mNested(nesting), + mContainANYPositionInInternalDimensions(false) { // 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?? @@ -80,10 +81,12 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat if (metric.has_dimensions_in_what()) { translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); } if (internalDimensions.has_field()) { translateFieldMatcher(internalDimensions, &mInternalDimensions); + mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions); } if (metric.has_dimensions_in_condition()) { @@ -100,7 +103,18 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat } } mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); - + mUnSlicedPartCondition = ConditionState::kUnknown; + + mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions); + if (mWizard != nullptr && mConditionTrackerIndex >= 0) { + mSameConditionDimensionsInTracker = + mWizard->equalOutputDimensions(mConditionTrackerIndex, mDimensionsInCondition); + if (mMetric2ConditionLinks.size() == 1) { + mHasLinksToAllConditionDimensionsInTracker = + mWizard->equalOutputDimensions(mConditionTrackerIndex, + mMetric2ConditionLinks.begin()->conditionFields); + } + } VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -109,9 +123,18 @@ DurationMetricProducer::~DurationMetricProducer() { VLOG("~DurationMetric() called"); } -sp DurationMetricProducer::addAnomalyTracker(const Alert &alert) { +sp DurationMetricProducer::addAnomalyTracker( + const Alert &alert, const sp& anomalyAlarmMonitor) { std::lock_guard lock(mMutex); - sp anomalyTracker = new DurationAnomalyTracker(alert, mConfigKey); + if (mAggregationType == DurationMetric_AggregationType_SUM) { + if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) { + ALOGW("invalid alert for SUM: 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, anomalyAlarmMonitor); if (anomalyTracker != nullptr) { mAnomalyTrackers.push_back(anomalyTracker); } @@ -125,12 +148,171 @@ unique_ptr DurationMetricProducer::createDurationTracker( return make_unique( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum, - mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); + mStartTimeNs, mBucketSizeNs, mConditionSliced, + mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum, - mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); + mStartTimeNs, mBucketSizeNs, mConditionSliced, + mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); + } +} + +// SlicedConditionChange optimization case 1: +// 1. If combination condition, logical operation is AND, only one sliced child predicate. +// 2. No condition in dimension +// 3. The links covers all dimension fields in the sliced child condition predicate. +void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(const uint64_t eventTime) { + if (mMetric2ConditionLinks.size() != 1 || + !mHasLinksToAllConditionDimensionsInTracker || + !mDimensionsInCondition.empty()) { + return; + } + + bool currentUnSlicedPartCondition = true; + if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) { + ConditionState unslicedPartState = + mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex); + // When the unsliced part is still false, return directly. + if (mUnSlicedPartCondition == ConditionState::kFalse && + unslicedPartState == ConditionState::kFalse) { + return; + } + mUnSlicedPartCondition = unslicedPartState; + currentUnSlicedPartCondition = mUnSlicedPartCondition > 0; + } + + auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex); + auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex); + + // The condition change is from the unsliced predicates. + // We need to find out the true dimensions from the sliced predicate and flip their condition + // state based on the new unsliced condition state. + if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || + (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { + std::set trueConditionDimensions; + mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions); + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + HashableDimensionKey linkedConditionDimensionKey; + getDimensionForCondition(whatIt.first.getValues(), + mMetric2ConditionLinks[0], + &linkedConditionDimensionKey); + if (trueConditionDimensions.find(linkedConditionDimensionKey) != + trueConditionDimensions.end()) { + for (auto& condIt : whatIt.second) { + condIt.second->onConditionChanged( + currentUnSlicedPartCondition, eventTime); + } + } + } + } else { + // Handle the condition change from the sliced predicate. + if (currentUnSlicedPartCondition) { + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + HashableDimensionKey linkedConditionDimensionKey; + getDimensionForCondition(whatIt.first.getValues(), + mMetric2ConditionLinks[0], + &linkedConditionDimensionKey); + if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) != + dimensionsChangedToTrue->end()) { + for (auto& condIt : whatIt.second) { + condIt.second->onConditionChanged(true, eventTime); + } + } + if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) != + dimensionsChangedToFalse->end()) { + for (auto& condIt : whatIt.second) { + condIt.second->onConditionChanged(false, eventTime); + } + } + } + } + } +} + + +// SlicedConditionChange optimization case 2: +// 1. If combination condition, logical operation is AND, only one sliced child predicate. +// 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate. +void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(const uint64_t eventTime) { + if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) { + return; + } + + auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex); + auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex); + + bool currentUnSlicedPartCondition = true; + if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) { + ConditionState unslicedPartState = + mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex); + // When the unsliced part is still false, return directly. + if (mUnSlicedPartCondition == ConditionState::kFalse && + unslicedPartState == ConditionState::kFalse) { + return; + } + mUnSlicedPartCondition = unslicedPartState; + currentUnSlicedPartCondition = mUnSlicedPartCondition > 0; + } + + const std::set* trueDimensionsToProcess = nullptr; + const std::set* falseDimensionsToProcess = nullptr; + + std::set currentTrueConditionDimensions; + if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || + (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { + mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, ¤tTrueConditionDimensions); + trueDimensionsToProcess = ¤tTrueConditionDimensions; + } else if (currentUnSlicedPartCondition) { + // Handles the condition change from the sliced predicate. If the unsliced condition state + // is not true, not need to do anything. + trueDimensionsToProcess = dimensionsChangedToTrue; + falseDimensionsToProcess = dimensionsChangedToFalse; + } + + if (trueDimensionsToProcess == nullptr && falseDimensionsToProcess == nullptr) { + return; + } + + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + if (falseDimensionsToProcess != nullptr) { + for (const auto& changedDim : *falseDimensionsToProcess) { + auto condIt = whatIt.second.find(changedDim); + if (condIt != whatIt.second.end()) { + condIt->second->onConditionChanged(false, eventTime); + } + } + } + if (trueDimensionsToProcess != nullptr) { + HashableDimensionKey linkedConditionDimensionKey; + if (!trueDimensionsToProcess->empty() && mMetric2ConditionLinks.size() == 1) { + getDimensionForCondition(whatIt.first.getValues(), + mMetric2ConditionLinks[0], + &linkedConditionDimensionKey); + } + for (auto& trueDim : *trueDimensionsToProcess) { + auto condIt = whatIt.second.find(trueDim); + if (condIt != whatIt.second.end()) { + condIt->second->onConditionChanged( + currentUnSlicedPartCondition, eventTime); + } else { + if (mMetric2ConditionLinks.size() == 0 || + trueDim.contains(linkedConditionDimensionKey)) { + if (!whatIt.second.empty()) { + unique_ptr newTracker = + whatIt.second.begin()->second->clone(eventTime); + if (newTracker != nullptr) { + newTracker->setEventKey( + MetricDimensionKey(whatIt.first, trueDim)); + newTracker->onConditionChanged(true, eventTime); + whatIt.second[trueDim] = std::move(newTracker); + } + } + } + } + } + } } } @@ -138,30 +320,83 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); flushIfNeededLocked(eventTime); - // Now for each of the on-going event, check if the condition has changed for them. - for (auto& pair : mCurrentSlicedDurationTrackerMap) { - pair.second->onSlicedConditionMayChange(eventTime); + if (!mConditionSliced) { + return; } + bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex); + if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker && + mDimensionsInCondition.empty()) { + onSlicedConditionMayChangeLocked_opt1(eventTime); + return; + } - std::unordered_set conditionDimensionsKeySet; - mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition, - &conditionDimensionsKeySet); + if (changeDimTrackable && mSameConditionDimensionsInTracker && + mMetric2ConditionLinks.size() <= 1) { + onSlicedConditionMayChangeLocked_opt2(eventTime); + return; + } - for (auto& pair : mCurrentSlicedDurationTrackerMap) { - conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition()); + // Now for each of the on-going event, check if the condition has changed for them. + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + for (auto& pair : whatIt.second) { + pair.second->onSlicedConditionMayChange(eventTime); + } } - std::unordered_set newKeys; - for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) { - for (auto& pair : mCurrentSlicedDurationTrackerMap) { - auto newKey = - MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey); - if (newKeys.find(newKey) == newKeys.end()) { - mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime); - mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey); - mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime); + + if (mDimensionsInCondition.empty()) { + return; + } + + if (mMetric2ConditionLinks.empty()) { + std::unordered_set conditionDimensionsKeySet; + mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition, + !mSameConditionDimensionsInTracker, + &conditionDimensionsKeySet); + for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) { + for (const auto& pair : whatIt.second) { + conditionDimensionsKeySet.erase(pair.first); + } + } + for (const auto& conditionDimension : conditionDimensionsKeySet) { + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + if (!whatIt.second.empty()) { + unique_ptr newTracker = + whatIt.second.begin()->second->clone(eventTime); + if (newTracker != nullptr) { + newTracker->setEventKey(MetricDimensionKey( + whatIt.first, conditionDimension)); + newTracker->onSlicedConditionMayChange(eventTime); + whatIt.second[conditionDimension] = std::move(newTracker); + } + } + } + } + } else { + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + ConditionKey conditionKey; + for (const auto& link : mMetric2ConditionLinks) { + getDimensionForCondition(whatIt.first.getValues(), link, + &conditionKey[link.conditionId]); + } + std::unordered_set conditionDimensionsKeys; + mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, + &conditionDimensionsKeys); + + for (const auto& conditionDimension : conditionDimensionsKeys) { + if (!whatIt.second.empty() && + whatIt.second.find(conditionDimension) == whatIt.second.end()) { + auto newTracker = whatIt.second.begin()->second->clone(eventTime); + if (newTracker != nullptr) { + newTracker->setEventKey( + MetricDimensionKey(whatIt.first, conditionDimension)); + newTracker->onSlicedConditionMayChange(eventTime); + whatIt.second[conditionDimension] = std::move(newTracker); + } + } } - newKeys.insert(newKey); } } } @@ -173,11 +408,18 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, flushIfNeededLocked(eventTime); // TODO: need to populate the condition change time from the event which triggers the condition // change, instead of using current time. - for (auto& pair : mCurrentSlicedDurationTrackerMap) { - pair.second->onConditionChanged(conditionMet, eventTime); + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + for (auto& pair : whatIt.second) { + pair.second->onConditionChanged(conditionMet, eventTime); + } } } +void DurationMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); +} + void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); @@ -187,25 +429,25 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); + uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS); VLOG("Duration metric %lld dump report now...", (long long)mMetricId); for (const auto& pair : mPastBuckets) { const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.c_str()); + VLOG(" dimension key %s", dimensionKey.toString().c_str()); - long long wrapperToken = + uint64_t wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. - long long dimensionToken = protoOutput->start( + uint64_t dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { - long long dimensionInConditionToken = protoOutput->start( + uint64_t dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); @@ -213,7 +455,7 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // Then fill bucket_info (DurationBucketInfo). for (const auto& bucket : pair.second) { - long long bucketInfoToken = protoOutput->start( + uint64_t bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); @@ -239,13 +481,21 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { return; } VLOG("flushing..........."); - for (auto it = mCurrentSlicedDurationTrackerMap.begin(); - it != mCurrentSlicedDurationTrackerMap.end();) { - if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) { - VLOG("erase bucket for key %s", it->first.c_str()); - it = mCurrentSlicedDurationTrackerMap.erase(it); + for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin(); + whatIt != mCurrentSlicedDurationTrackerMap.end();) { + for (auto it = whatIt->second.begin(); it != whatIt->second.end();) { + if (it->second->flushIfNeeded(eventTimeNs, &mPastBuckets)) { + VLOG("erase bucket for key %s %s", + whatIt->first.toString().c_str(), it->first.toString().c_str()); + it = whatIt->second.erase(it); + } else { + ++it; + } + } + if (whatIt->second.empty()) { + whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt); } else { - ++it; + whatIt++; } } @@ -255,13 +505,21 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { } 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); + for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin(); + whatIt != mCurrentSlicedDurationTrackerMap.end();) { + for (auto it = whatIt->second.begin(); it != whatIt->second.end();) { + if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) { + VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(), + it->first.toString().c_str()); + it = whatIt->second.erase(it); + } else { + ++it; + } + } + if (whatIt->second.empty()) { + whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt); } else { - ++it; + whatIt++; } } } @@ -274,18 +532,17 @@ void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId, (unsigned long)mCurrentSlicedDurationTrackerMap.size()); if (verbose) { - for (const auto& slice : mCurrentSlicedDurationTrackerMap) { - fprintf(out, "\t%s\n", slice.first.c_str()); - slice.second->dumpStates(out, verbose); + for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) { + for (const auto& slice : whatIt.second) { + fprintf(out, "\t(what)%s\t(condition)%s\n", whatIt.first.toString().c_str(), + slice.first.toString().c_str()); + slice.second->dumpStates(out, verbose); + } } } } bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - // the key is not new, we are good. - if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) { - return false; - } // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; @@ -293,54 +550,277 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("DurationMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.c_str()); + (long long)mMetricId, newKey.toString().c_str()); return true; } } return false; } +void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey, + const ConditionKey& conditionKeys, + bool condition, const LogEvent& event) { + const auto& whatKey = eventKey.getDimensionKeyInWhat(); + const auto& condKey = eventKey.getDimensionKeyInCondition(); + + auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); + if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { + if (hitGuardRailLocked(eventKey)) { + return; + } + mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey); + } else { + if (whatIt->second.find(condKey) == whatIt->second.end()) { + if (hitGuardRailLocked(eventKey)) { + return; + } + mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey); + } + } + + auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(condKey); + if (mUseWhatDimensionAsInternalDimension) { + it->second->noteStart(whatKey, condition, + event.GetElapsedTimestampNs(), conditionKeys); + return; + } + + if (mInternalDimensions.empty()) { + it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, + event.GetElapsedTimestampNs(), conditionKeys); + } else { + if (mContainANYPositionInInternalDimensions) { + std::vector dimensionKeys; + filterValues(mInternalDimensions, event.getValues(), &dimensionKeys); + for (const auto& key : dimensionKeys) { + it->second->noteStart(key, condition, event.GetElapsedTimestampNs(), conditionKeys); + } + } else { + HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY; + filterValues(mInternalDimensions, event.getValues(), &dimensionKey); + it->second->noteStart( + dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys); + } + } + +} + void DurationMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, bool condition, const LogEvent& event) { + ALOGW("Not used in duration tracker."); +} + +void DurationMetricProducer::onMatchedLogEventLocked_simple(const size_t matcherIndex, + const LogEvent& event) { + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); + if (eventTimeNs < mStartTimeNs) { + return; + } + flushIfNeededLocked(event.GetElapsedTimestampNs()); + // Handles Stopall events. if (matcherIndex == mStopAllIndex) { - for (auto& pair : mCurrentSlicedDurationTrackerMap) { - pair.second->noteStopAll(event.GetElapsedTimestampNs()); + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + for (auto& pair : whatIt.second) { + pair.second->noteStopAll(event.GetElapsedTimestampNs()); + } } return; } - if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) { - if (hitGuardRailLocked(eventKey)) { + HashableDimensionKey dimensionInWhat; + if (!mDimensionsInWhat.empty()) { + filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); + } else { + dimensionInWhat = DEFAULT_DIMENSION_KEY; + } + + // Handles Stop events. + if (matcherIndex == mStopIndex) { + if (mUseWhatDimensionAsInternalDimension) { + auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + for (const auto& condIt : whatIt->second) { + condIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); + } + } return; } - mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey); + + HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; + if (!mInternalDimensions.empty()) { + filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); + } + + auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + for (const auto& condIt : whatIt->second) { + condIt.second->noteStop( + internalDimensionKey, event.GetElapsedTimestampNs(), false); + } + } + return; } - auto it = mCurrentSlicedDurationTrackerMap.find(eventKey); + bool condition; + ConditionKey conditionKey; + std::unordered_set dimensionKeysInCondition; + if (mConditionSliced) { + for (const auto& link : mMetric2ConditionLinks) { + getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); + } - std::vector values; - filterValues(mInternalDimensions, event.getValues(), &values); - if (values.empty()) { - if (matcherIndex == mStartIndex) { - it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, - event.GetElapsedTimestampNs(), conditionKeys); - } else if (matcherIndex == mStopIndex) { - it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetElapsedTimestampNs(), false); + auto conditionState = + mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, + &dimensionKeysInCondition); + condition = (conditionState == ConditionState::kTrue); + if (mDimensionsInCondition.empty() && condition) { + dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); + } + } else { + condition = mCondition; + if (condition) { + dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } + } + + if (dimensionKeysInCondition.empty()) { + handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY), + conditionKey, condition, event); } else { - for (const auto& value : values) { - if (matcherIndex == mStartIndex) { - it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys); - } else if (matcherIndex == mStopIndex) { - it->second->noteStop(value, event.GetElapsedTimestampNs(), false); + auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); + // If the what dimension is already there, we should update all the trackers even + // the condition is false. + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + for (const auto& condIt : whatIt->second) { + const bool cond = dimensionKeysInCondition.find(condIt.first) != + dimensionKeysInCondition.end(); + handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first), + conditionKey, cond, event); + dimensionKeysInCondition.erase(condIt.first); } } + for (const auto& conditionDimension : dimensionKeysInCondition) { + handleStartEvent(MetricDimensionKey(dimensionInWhat, conditionDimension), conditionKey, + condition, event); + } } +} +void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, + const LogEvent& event) { + if (!mContainANYPositionInDimensionsInWhat) { + onMatchedLogEventLocked_simple(matcherIndex, event); + return; + } + + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); + if (eventTimeNs < mStartTimeNs) { + return; + } + + flushIfNeededLocked(event.GetElapsedTimestampNs()); + + // Handles Stopall events. + if (matcherIndex == mStopAllIndex) { + for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { + for (auto& pair : whatIt.second) { + pair.second->noteStopAll(event.GetElapsedTimestampNs()); + } + } + return; + } + + vector dimensionInWhatValues; + if (!mDimensionsInWhat.empty()) { + filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues); + } else { + dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY); + } + + // Handles Stop events. + if (matcherIndex == mStopIndex) { + if (mUseWhatDimensionAsInternalDimension) { + for (const HashableDimensionKey& whatKey : dimensionInWhatValues) { + auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + for (const auto& condIt : whatIt->second) { + condIt.second->noteStop(whatKey, event.GetElapsedTimestampNs(), false); + } + } + } + return; + } + + HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; + if (!mInternalDimensions.empty()) { + filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); + } + + for (const HashableDimensionKey& whatDimension : dimensionInWhatValues) { + auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension); + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + for (const auto& condIt : whatIt->second) { + condIt.second->noteStop( + internalDimensionKey, event.GetElapsedTimestampNs(), false); + } + } + } + return; + } + + bool condition; + ConditionKey conditionKey; + std::unordered_set dimensionKeysInCondition; + if (mConditionSliced) { + for (const auto& link : mMetric2ConditionLinks) { + getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); + } + + auto conditionState = + mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, + &dimensionKeysInCondition); + condition = (conditionState == ConditionState::kTrue); + if (mDimensionsInCondition.empty() && condition) { + dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); + } + } else { + condition = mCondition; + if (condition) { + dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); + } + } + + for (const auto& whatDimension : dimensionInWhatValues) { + if (dimensionKeysInCondition.empty()) { + handleStartEvent(MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY), + conditionKey, condition, event); + } else { + auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatDimension); + // If the what dimension is already there, we should update all the trackers even + // the condition is false. + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + for (const auto& condIt : whatIt->second) { + const bool cond = dimensionKeysInCondition.find(condIt.first) != + dimensionKeysInCondition.end(); + handleStartEvent(MetricDimensionKey(whatDimension, condIt.first), + conditionKey, cond, event); + dimensionKeysInCondition.erase(condIt.first); + } + } + for (const auto& conditionDimension : dimensionKeysInCondition) { + handleStartEvent(MetricDimensionKey(whatDimension, conditionDimension), + conditionKey, condition, event); + } + } + } } size_t DurationMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 5f29281a8a3c1715c5c2af18f13ea0c5959777bf..6746e116333f08c08f57460b71fc7bc57ca68e4b 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -46,15 +46,23 @@ public: virtual ~DurationMetricProducer(); - sp addAnomalyTracker(const Alert &alert) override; + sp addAnomalyTracker(const Alert &alert, + const sp& anomalyAlarmMonitor) override; protected: + void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override; + + void onMatchedLogEventLocked_simple(const size_t matcherIndex, const LogEvent& event); + void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, bool condition, const LogEvent& event) override; private: + void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, + bool condition, const LogEvent& event); + void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; @@ -64,11 +72,16 @@ private: // Internal interface to handle sliced condition change. void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override; + void onSlicedConditionMayChangeLocked_opt1(const uint64_t eventTime); + void onSlicedConditionMayChangeLocked_opt2(const uint64_t eventTime); + // Internal function to calculate the current used bytes. size_t byteSizeLocked() const override; void dumpStatesLocked(FILE* out, bool verbose) const override; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime); @@ -91,12 +104,21 @@ private: // The dimension from the atom predicate. e.g., uid, wakelock name. vector mInternalDimensions; + bool mContainANYPositionInInternalDimensions; + + // This boolean is true iff When mInternalDimensions == mDimensionsInWhat + bool mUseWhatDimensionAsInternalDimension; + + // Caches the current unsliced part condition. + ConditionState mUnSlicedPartCondition; + // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. std::unordered_map> mPastBuckets; - // The current bucket. - std::unordered_map> + // The duration trackers in the current bucket. + std::unordered_map>> mCurrentSlicedDurationTrackerMap; // Helper function to create a duration tracker given the metric aggregation type. diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 96d0cfcc5897861b62d6a1edf9fe90d6556e14da..778eb8e18896591580c0458cca321499d6cc0c7c 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -75,6 +75,10 @@ EventMetricProducer::~EventMetricProducer() { VLOG("~EventMetricProducer() called"); } +void EventMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + mProto->clear(); +} + void EventMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { } @@ -127,7 +131,7 @@ void EventMetricProducer::onMatchedLogEventInternalLocked( return; } - long long wrapperToken = + uint64_t wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); const bool truncateTimestamp = android::util::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) == @@ -144,7 +148,7 @@ void EventMetricProducer::onMatchedLogEventInternalLocked( (long long)getWallClockNs()); } - long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); + uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); event.ToProto(*mProto); mProto->end(eventToken); mProto->end(wrapperToken); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 3f2c5a54e02292b142e669f8f74ddebbe2e2267c..d6f81fda6ff6da5216914679af9331639b929a49 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -55,6 +55,8 @@ private: // Internal interface to handle sliced condition change. void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Internal function to calculate the current used bytes. size_t byteSizeLocked() const override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 7d09ff9ffc8e9c8ddbbb604239df99dc2017762a..e479e5caec2f4b7e2e2ebb10985f516dc612e57b 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -83,6 +83,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric // TODO: use UidMap if uid->pkg_name is required if (metric.has_dimensions_in_what()) { translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); } if (metric.has_dimensions_in_condition()) { @@ -135,23 +136,23 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); + uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); for (const auto& pair : mPastBuckets) { const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.c_str()); - long long wrapperToken = + VLOG(" dimension key %s", dimensionKey.toString().c_str()); + uint64_t wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. - long long dimensionToken = protoOutput->start( + uint64_t dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { - long long dimensionInConditionToken = protoOutput->start( + uint64_t dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); @@ -159,7 +160,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // Then fill bucket_info (GaugeBucketInfo). for (const auto& bucket : pair.second) { - long long bucketInfoToken = protoOutput->start( + uint64_t bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); @@ -167,7 +168,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, (long long)bucket.mBucketEndNs); if (!bucket.mGaugeAtoms.empty()) { - long long atomsToken = + uint64_t atomsToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ATOM); for (const auto& atom : bucket.mGaugeAtoms) { writeFieldValueTreeToStream(mTagId, *(atom.mFields), protoOutput); @@ -283,7 +284,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("GaugeMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.c_str()); + (long long)mMetricId, newKey.toString().c_str()); return true; } } @@ -353,6 +354,11 @@ void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { } } +void GaugeMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); +} + // 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. @@ -393,7 +399,8 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) 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.toString().c_str()); } // If we have anomaly trackers, we need to update the partial bucket values. diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index c3ae6ce921d264647a5a2f43c3fa75d3d3996dc1..ca8dc7582680ad9f54bb5b6eb929ea0a0557eb2f 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -108,6 +108,8 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime) override; diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index f3307dc1d1e3789ef90f45fa6d9eb1f2e82c80ec..6c90b031a9cce02fc245c75f6c336c1c1f548df0 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -37,48 +37,58 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo std::unordered_set dimensionKeysInCondition; if (mConditionSliced) { for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event, link, &conditionKey[link.conditionId]); + getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); } - auto conditionState = mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, &dimensionKeysInCondition); condition = (conditionState == ConditionState::kTrue); } else { condition = mCondition; } - vector dimensionInWhatValues; - if (mDimensionsInWhat.size() > 0) { - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues); + if (mDimensionsInCondition.empty() && condition) { + dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY); } - if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) { - onMatchedLogEventInternalLocked( - matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event); - } else if (dimensionKeysInCondition.empty()) { - for (const HashableDimensionKey& whatValue : dimensionInWhatValues) { - onMatchedLogEventInternalLocked(matcherIndex, - MetricDimensionKey(whatValue, DEFAULT_DIMENSION_KEY), - conditionKey, condition, event); - } - } else if (dimensionInWhatValues.empty()) { - for (const auto& conditionDimensionKey : dimensionKeysInCondition) { - onMatchedLogEventInternalLocked( - matcherIndex, - MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey), - conditionKey, condition, event); + if (mContainANYPositionInDimensionsInWhat) { + vector dimensionInWhatValues; + if (!mDimensionsInWhat.empty()) { + filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues); + } else { + dimensionInWhatValues.push_back(DEFAULT_DIMENSION_KEY); } - } else { - for (const auto& whatValue : dimensionInWhatValues) { + + for (const auto& whatDimension : dimensionInWhatValues) { for (const auto& conditionDimensionKey : dimensionKeysInCondition) { onMatchedLogEventInternalLocked( - matcherIndex, MetricDimensionKey(whatValue, conditionDimensionKey), + matcherIndex, MetricDimensionKey(whatDimension, conditionDimensionKey), conditionKey, condition, event); } + if (dimensionKeysInCondition.empty()) { + onMatchedLogEventInternalLocked( + matcherIndex, MetricDimensionKey(whatDimension, DEFAULT_DIMENSION_KEY), + conditionKey, condition, event); + } + } + } else { + HashableDimensionKey dimensionInWhat; + filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); + MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY); + for (const auto& conditionDimensionKey : dimensionKeysInCondition) { + metricKey.setDimensionKeyInCondition(conditionDimensionKey); + onMatchedLogEventInternalLocked( + matcherIndex, metricKey, conditionKey, condition, event); + } + if (dimensionKeysInCondition.empty()) { + onMatchedLogEventInternalLocked( + matcherIndex, metricKey, conditionKey, condition, event); } } -} + + } } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 8663e5eed7a71907a4c12b8be2ad384da72ad0f3..ea45f43a2d29cd9a6c6d12d043d8c6c69d2d296b 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -50,7 +50,11 @@ public: mCondition(conditionIndex >= 0 ? false : true), mConditionSliced(false), mWizard(wizard), - mConditionTrackerIndex(conditionIndex){}; + mConditionTrackerIndex(conditionIndex), + mContainANYPositionInDimensionsInWhat(false), + mSameConditionDimensionsInTracker(false), + mHasLinksToAllConditionDimensionsInTracker(false) { + } virtual ~MetricProducer(){}; @@ -124,7 +128,8 @@ public: } /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */ - virtual sp addAnomalyTracker(const Alert &alert) { + virtual sp addAnomalyTracker(const Alert &alert, + const sp& anomalyAlarmMonitor) { std::lock_guard lock(mMutex); sp anomalyTracker = new AnomalyTracker(alert, mConfigKey); if (anomalyTracker != nullptr) { @@ -147,6 +152,15 @@ public: return mMetricId; } + // Let MetricProducer drop in-memory data to save memory. + // We still need to keep future data valid and anomaly tracking work, which means we will + // have to flush old data, informing anomaly trackers then safely drop old data. + // We still keep current bucket data for future metrics' validity. + void dropData(const uint64_t dropTimeNs) { + std::lock_guard lock(mMutex); + dropDataLocked(dropTimeNs); + } + protected: virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0; @@ -178,6 +192,8 @@ protected: return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; } + virtual void dropDataLocked(const uint64_t dropTimeNs) = 0; + const int64_t mMetricId; const ConfigKey mConfigKey; @@ -207,6 +223,16 @@ protected: vector mDimensionsInWhat; // The dimensions_in_what defined in statsd_config vector mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config + bool mContainANYPositionInDimensionsInWhat; + + // True iff the condition dimensions equal to the sliced dimensions in the simple condition + // tracker. This field is always false for combinational condition trackers. + bool mSameConditionDimensionsInTracker; + + // True iff the metric to condition links cover all dimension fields in the condition tracker. + // This field is always false for combinational condition trackers. + bool mHasLinksToAllConditionDimensionsInTracker; + std::vector mMetric2ConditionLinks; std::vector> mAnomalyTrackers; @@ -232,7 +258,7 @@ protected: const LogEvent& event) = 0; // Consume the parsed stats log entry that already matched the "what" of the metric. - void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); + virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); mutable std::mutex mMutex; }; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index e75b710cc9db9717890bef72a85719e27ee5c059..1ca59a366fcb17bdd63f43c8755cee8405f9ed98 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -49,13 +49,17 @@ namespace statsd { const int FIELD_ID_METRICS = 1; MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, - const long timeBaseSec, sp uidMap) + const long timeBaseSec, + const sp &uidMap, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor) : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(timeBaseSec * NS_PER_SEC) { mConfigValid = - initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, - mAllConditionTrackers, - mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, - mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); + initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, mTagIds, mAllAtomMatchers, + mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, + mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, + mTrackerToConditionMap, mNoReportMetricIds); if (config.allowed_log_source_size() == 0) { // TODO(b/70794411): uncomment the following line and remove the hard coded log source @@ -99,7 +103,9 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(), mAllConditionTrackers.size(), - mAllAtomMatchers.size(), 0, mConfigValid); + mAllAtomMatchers.size(), + mAllAnomalyTrackers.size(), + mConfigValid); } MetricsManager::~MetricsManager() { @@ -169,12 +175,18 @@ void MetricsManager::dumpStates(FILE* out, bool verbose) { } } +void MetricsManager::dropData(const uint64_t dropTimeNs) { + for (const auto& producer : mAllMetricProducers) { + producer->dropData(dropTimeNs); + } +} + void MetricsManager::onDumpReport(const uint64_t dumpTimeStampNs, ProtoOutputStream* protoOutput) { VLOG("=========================Metric Reports Start=========================="); // one StatsLogReport per MetricProduer for (const auto& producer : mAllMetricProducers) { if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { - long long token = + uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); producer->onDumpReport(dumpTimeStampNs, protoOutput); protoOutput->end(token); @@ -198,31 +210,59 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // Uid is 3rd from last field and must match the caller's uid, // unless that caller is statsd itself (statsd is allowed to spoof uids). long appHookUid = event.GetLong(event.size()-2, &err); + if (err != NO_ERROR ) { + VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid"); + return; + } int32_t loggerUid = event.GetUid(); - if (err != NO_ERROR || (loggerUid != appHookUid && loggerUid != AID_STATSD)) { - VLOG("AppHook has invalid uid: claimed %ld but caller is %d", appHookUid, loggerUid); + if (loggerUid != appHookUid && loggerUid != AID_STATSD) { + VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d", + appHookUid, loggerUid); return; } // Label is 2nd from last field and must be from [0, 15]. long appHookLabel = event.GetLong(event.size()-1, &err); - if (err != NO_ERROR || appHookLabel < 0 || appHookLabel > 15) { - VLOG("AppHook does not have valid label %ld", appHookLabel); + if (err != NO_ERROR ) { + VLOG("APP_BREADCRUMB_REPORTED had error when parsing the label field"); + return; + } else if (appHookLabel < 0 || appHookLabel > 15) { + VLOG("APP_BREADCRUMB_REPORTED does not have valid label %ld", appHookLabel); return; } // The state must be from 0,3. This part of code must be manually updated. long appHookState = event.GetLong(event.size(), &err); - if (err != NO_ERROR || appHookState < 0 || appHookState > 3) { - VLOG("AppHook does not have valid state %ld", appHookState); + if (err != NO_ERROR ) { + VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field"); + return; + } else if (appHookState < 0 || appHookState > 3) { + VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); return; } } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. // Check that the davey duration is reasonable. Max length check is for privacy. status_t err = NO_ERROR; + + // Uid is the first field provided. + long jankUid = event.GetLong(1, &err); + if (err != NO_ERROR ) { + VLOG("Davey occurred had error when parsing the uid"); + return; + } + int32_t loggerUid = event.GetUid(); + if (loggerUid != jankUid && loggerUid != AID_STATSD) { + VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid, + loggerUid); + return; + } + long duration = event.GetLong(event.size(), &err); - if (err != NO_ERROR || duration > 100000) { + if (err != NO_ERROR ) { + VLOG("Davey occurred had error when parsing the duration"); + return; + } else if (duration > 100000) { VLOG("Davey duration is unreasonably long: %ld", duration); return; } @@ -312,16 +352,19 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } } -void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs, - unordered_set, SpHash>& anomalySet) { +void MetricsManager::onAnomalyAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash>& alarmSet) { for (const auto& itr : mAllAnomalyTrackers) { - itr->informAlarmsFired(timestampNs, anomalySet); + itr->informAlarmsFired(timestampNs, alarmSet); } } -void MetricsManager::setAnomalyMonitor(const sp& anomalyMonitor) { - for (auto& itr : mAllAnomalyTrackers) { - itr->setAnomalyMonitor(anomalyMonitor); +void MetricsManager::onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash>& alarmSet) { + for (const auto& itr : mAllPeriodicAlarmTrackers) { + itr->informAlarmsFired(timestampNs, alarmSet); } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index d4f844fe386c23891ca563fdb585dc994c07275b..dbab81431743b074096946914221666fe3bcd5e8 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -16,7 +16,8 @@ #pragma once -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" +#include "anomaly/AlarmTracker.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionTracker.h" #include "config/ConfigKey.h" @@ -36,7 +37,8 @@ namespace statsd { class MetricsManager : public PackageInfoListener { public: MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const long timeBaseSec, - sp uidMap); + const sp& uidMap, const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor); virtual ~MetricsManager(); @@ -47,9 +49,11 @@ public: void onAnomalyAlarmFired( const uint64_t timestampNs, - unordered_set, SpHash>& anomalySet); + unordered_set, SpHash>& alarmSet); - void setAnomalyMonitor(const sp& anomalyMonitor); + void onPeriodicAlarmFired( + const uint64_t timestampNs, + unordered_set, SpHash>& alarmSet); void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid, const int64_t version) override; @@ -69,6 +73,8 @@ public: return mLastReportTimeNs; }; + virtual void dropData(const uint64_t dropTimeNs); + // Config source owner can call onDumpReport() to get all the metrics collected. virtual void onDumpReport(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput); @@ -120,6 +126,9 @@ private: // Hold all alert trackers. std::vector> mAllAnomalyTrackers; + // Hold all periodic alarm trackers. + std::vector> mAllPeriodicAlarmTrackers; + // To make the log processing more efficient, we want to do as much filtering as possible // before we go into individual trackers and conditions to match. @@ -151,10 +160,18 @@ private: FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice); FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink); - FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink); - FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink); - FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition); + + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition); + }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 35fcdc48a08ad762c78ae0debfa137bd9b31beab..09913dc513fd140499b5709a2ba70af739e158b0 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -80,6 +80,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric mBucketSizeNs = bucketSizeMills * 1000000; if (metric.has_dimensions_in_what()) { translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat); + mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what()); } if (metric.has_dimensions_in_condition()) { @@ -129,6 +130,11 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } +void ValueMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); +} + void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("metric %lld dump report now...", (long long)mMetricId); @@ -137,21 +143,21 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, return; } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); - long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); + uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); for (const auto& pair : mPastBuckets) { const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", dimensionKey.c_str()); - long long wrapperToken = + VLOG(" dimension key %s", dimensionKey.toString().c_str()); + uint64_t wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. - long long dimensionToken = protoOutput->start( + uint64_t dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput); protoOutput->end(dimensionToken); if (dimensionKey.hasDimensionKeyInCondition()) { - long long dimensionInConditionToken = protoOutput->start( + uint64_t dimensionInConditionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput); protoOutput->end(dimensionInConditionToken); @@ -159,7 +165,7 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // Then fill bucket_info (ValueBucketInfo). for (const auto& bucket : pair.second) { - long long bucketInfoToken = protoOutput->start( + uint64_t bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, (long long)bucket.mBucketStartNs); @@ -249,7 +255,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("ValueMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.c_str()); + (long long)mMetricId, newKey.toString().c_str()); return true; } } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index b518f2f841cfbfe465b5999eb69daf54d453d105..5e42bd255df40e4a4738a4490ff33927f55152b0 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -106,6 +106,8 @@ private: void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override; + void dropDataLocked(const uint64_t dropTimeNs) override; + const FieldMatcher mValueField; std::shared_ptr mStatsPullerManager; diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 8f236fa8bab62156a2b3568268e2dc2e445645a3..7b3393fda7e6ef02e7bdf4377bc0fd89a4540114 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -64,7 +64,7 @@ public: sp wizard, int conditionIndex, const std::vector& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, - uint64_t bucketSizeNs, bool conditionSliced, + uint64_t bucketSizeNs, bool conditionSliced, bool fullLink, const std::vector>& anomalyTrackers) : mConfigKey(key), mTrackerId(id), @@ -80,6 +80,7 @@ public: mCurrentBucketNum(currentBucketNum), mStartTimeNs(startTimeNs), mConditionSliced(conditionSliced), + mHasLinksToAllConditionDimensionsInTracker(fullLink), mAnomalyTrackers(anomalyTrackers){}; virtual ~DurationTracker(){}; @@ -198,6 +199,9 @@ protected: const bool mConditionSliced; + bool mSameConditionDimensionsInTracker; + bool mHasLinksToAllConditionDimensionsInTracker; + std::vector> mAnomalyTrackers; FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index b225560e76a8c3d35a61d6f1f2108a3ee6cbcdbe..8e0bf2678fea228e86b197ed9ca25cd5acb0c638 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -30,20 +30,33 @@ MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, const vector& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, - bool conditionSliced, + bool conditionSliced, bool fullLink, const vector>& anomalyTrackers) : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting, currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs, - conditionSliced, anomalyTrackers) { + conditionSliced, fullLink, anomalyTrackers) { + if (mWizard != nullptr) { + mSameConditionDimensionsInTracker = + mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition); + } } unique_ptr MaxDurationTracker::clone(const uint64_t eventTime) { auto clonedTracker = make_unique(*this); - for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) { - it->second.lastStartTime = eventTime; - it->second.lastDuration = 0; + for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) { + if (it->second.state != kStopped) { + it->second.lastStartTime = eventTime; + it->second.lastDuration = 0; + it++; + } else { + it = clonedTracker->mInfos.erase(it); + } + } + if (clonedTracker->mInfos.empty()) { + return nullptr; + } else { + return clonedTracker; } - return clonedTracker; } bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -59,7 +72,7 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 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", - (long long)mTrackerId, newKey.c_str()); + (long long)mTrackerId, newKey.toString().c_str()); return true; } } @@ -77,7 +90,7 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi if (mConditionSliced) { duration.conditionKeys = conditionKey; } - VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition); + VLOG("MaxDuration: key %s start condition %d", key.toString().c_str(), condition); switch (duration.state) { case kStarted: @@ -103,7 +116,7 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime, bool forceStop) { - VLOG("MaxDuration: key %s stop", key.c_str()); + VLOG("MaxDuration: key %s stop", key.toString().c_str()); if (mInfos.find(key) == mInfos.end()) { // we didn't see a start event before. do nothing. return; @@ -120,7 +133,7 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ stopAnomalyAlarm(); duration.state = DurationState::kStopped; int64_t durationTime = eventTime - duration.lastStartTime; - VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), + VLOG("Max, key %s, Stop %lld %lld %lld", key.toString().c_str(), (long long)duration.lastStartTime, (long long)eventTime, (long long)durationTime); duration.lastDuration += durationTime; @@ -241,13 +254,15 @@ void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { std::unordered_set conditionDimensionKeySet; ConditionState conditionState = mWizard->query( mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, &conditionDimensionKeySet); bool conditionMet = (conditionState == ConditionState::kTrue) && (mDimensionInCondition.size() == 0 || conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != conditionDimensionKeySet.end()); - VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet); + VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet); noteConditionChanged(pair.first, conditionMet, timestamp); } } @@ -277,7 +292,7 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b // 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()); + VLOG("MaxDurationTracker Key: %s Started->Paused ", key.toString().c_str()); } break; case kStopped: @@ -290,7 +305,7 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b it->second.state = DurationState::kStarted; it->second.lastStartTime = timestamp; startAnomalyAlarm(timestamp); - VLOG("MaxDurationTracker Key: %s Paused->Started", key.c_str()); + VLOG("MaxDurationTracker Key: %s Paused->Started", key.toString().c_str()); } break; } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index c731b75018811b5f5a6e2bb1b88d471824292275..0452d373d413cc8253e4a548b7b7714f035fe8e2 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -33,6 +33,7 @@ public: const std::vector& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, + bool fullLink, const std::vector>& anomalyTrackers); MaxDurationTracker(const MaxDurationTracker& tracker) = default; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index f583f91608b8ceead7623b968c3d7bba87945e1b..2358415dfaf718c60658fd5c3cb59d2f6bfed010 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -28,14 +28,18 @@ OringDurationTracker::OringDurationTracker( const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp wizard, int conditionIndex, const vector& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, - uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, + uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, bool fullLink, const vector>& anomalyTrackers) : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting, currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs, - conditionSliced, anomalyTrackers), + conditionSliced, fullLink, anomalyTrackers), mStarted(), mPaused() { mLastStartTime = 0; + if (mWizard != nullptr) { + mSameConditionDimensionsInTracker = + mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition); + } } unique_ptr OringDurationTracker::clone(const uint64_t eventTime) { @@ -57,7 +61,7 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // 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", - (long long)mTrackerId, newKey.c_str()); + (long long)mTrackerId, newKey.toString().c_str()); return true; } } @@ -83,13 +87,13 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) { mConditionKeyMap[key] = conditionKey; } - VLOG("Oring: %s start, condition %d", key.c_str(), condition); + VLOG("Oring: %s start, condition %d", key.toString().c_str(), condition); } void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp, const bool stopAll) { declareAnomalyIfAlarmExpired(timestamp); - VLOG("Oring: %s stop", key.c_str()); + VLOG("Oring: %s stop", key.toString().c_str()); auto it = mStarted.find(key); if (it != mStarted.end()) { (it->second)--; @@ -217,22 +221,26 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) if (!mStarted.empty()) { for (auto it = mStarted.begin(); it != mStarted.end();) { const auto& key = it->first; - if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { - VLOG("Key %s dont have condition key", key.c_str()); + const auto& condIt = mConditionKeyMap.find(key); + if (condIt == mConditionKeyMap.end()) { + VLOG("Key %s dont have condition key", key.toString().c_str()); ++it; continue; } std::unordered_set conditionDimensionKeySet; ConditionState conditionState = - mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], - mDimensionInCondition, &conditionDimensionKeySet); + mWizard->query(mConditionTrackerIndex, condIt->second, + mDimensionInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, + &conditionDimensionKeySet); if (conditionState != ConditionState::kTrue || (mDimensionInCondition.size() != 0 && conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) == conditionDimensionKeySet.end())) { startedToPaused.push_back(*it); it = mStarted.erase(it); - VLOG("Key %s started -> paused", key.c_str()); + VLOG("Key %s started -> paused", key.toString().c_str()); } else { ++it; } @@ -250,21 +258,24 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) for (auto it = mPaused.begin(); it != mPaused.end();) { const auto& key = it->first; if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { - VLOG("Key %s dont have condition key", key.c_str()); + VLOG("Key %s dont have condition key", key.toString().c_str()); ++it; continue; } std::unordered_set conditionDimensionKeySet; ConditionState conditionState = mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], - mDimensionInCondition, &conditionDimensionKeySet); + mDimensionInCondition, + !mSameConditionDimensionsInTracker, + !mHasLinksToAllConditionDimensionsInTracker, + &conditionDimensionKeySet); if (conditionState == ConditionState::kTrue && (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()); + VLOG("Key %s paused -> started", key.toString().c_str()); } else { ++it; } diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 07c13294fa449d50e0336f9b7c13e75180165571..610e3ea79aa1210b56f50237a46ea78cb0c9cf1a 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -32,6 +32,7 @@ public: int conditionIndex, const std::vector& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced, + bool fullLink, const std::vector>& anomalyTrackers); OringDurationTracker(const OringDurationTracker& tracker) = default; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 71e5c33b1b886e16eb9286702edb5e18580effc8..b5afef2502f86cb5f06c972886339c556075abc9 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -17,17 +17,22 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" +#include "metrics_manager_util.h" + #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" +#include "../condition/StateTracker.h" #include "../external/StatsPullerManager.h" #include "../matchers/CombinationLogMatchingTracker.h" #include "../matchers/SimpleLogMatchingTracker.h" -#include "CountMetricProducer.h" -#include "DurationMetricProducer.h" -#include "EventMetricProducer.h" -#include "GaugeMetricProducer.h" -#include "ValueMetricProducer.h" +#include "../metrics/CountMetricProducer.h" +#include "../metrics/DurationMetricProducer.h" +#include "../metrics/EventMetricProducer.h" +#include "../metrics/GaugeMetricProducer.h" +#include "../metrics/ValueMetricProducer.h" + #include "stats_util.h" +#include "statslog.h" using std::set; using std::string; @@ -154,6 +159,49 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, return true; } +/** + * A StateTracker is built from a SimplePredicate which has only "start", and no "stop" + * or "stop_all". The start must be an atom matcher that matches a state atom. It must + * have dimension, the dimension must be the state atom's primary fields plus exclusive state + * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState. + * + */ +bool isStateTracker(const SimplePredicate& simplePredicate, vector* primaryKeys) { + // 1. must not have "stop". must have "dimension" + if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) { + // TODO: need to check the start atom matcher too. + auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field()); + // 2. must be based on a state atom. + if (it != android::util::kStateAtomsFieldOptions.end()) { + // 3. dimension must be primary fields + state field IN ORDER + size_t expectedDimensionCount = it->second.primaryFields.size() + 1; + vector dimensions; + translateFieldMatcher(simplePredicate.dimensions(), &dimensions); + if (dimensions.size() != expectedDimensionCount) { + return false; + } + // 3.1 check the primary fields first. + size_t index = 0; + for (const auto& field : it->second.primaryFields) { + Matcher matcher = getSimpleMatcher(it->first, field); + if (!(matcher == dimensions[index])) { + return false; + } + primaryKeys->push_back(matcher); + index++; + } + Matcher stateFieldMatcher = + getSimpleMatcher(it->first, it->second.exclusiveField); + // 3.2 last dimension should be the exclusive field. + if (!(dimensions.back() == stateFieldMatcher)) { + return false; + } + return true; + } + } + return false; +} // namespace statsd + bool initConditions(const ConfigKey& key, const StatsdConfig& config, const unordered_map& logTrackerMap, unordered_map& conditionTrackerMap, @@ -169,8 +217,16 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, int index = allConditionTrackers.size(); switch (condition.contents_case()) { case Predicate::ContentsCase::kSimplePredicate: { - allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.id(), index, condition.simple_predicate(), logTrackerMap)); + vector primaryKeys; + if (isStateTracker(condition.simple_predicate(), &primaryKeys)) { + allConditionTrackers.push_back(new StateTracker(key, condition.id(), index, + condition.simple_predicate(), + logTrackerMap, primaryKeys)); + } else { + allConditionTrackers.push_back(new SimpleConditionTracker( + key, condition.id(), index, condition.simple_predicate(), + logTrackerMap)); + } break; } case Predicate::ContentsCase::kCombination: { @@ -494,6 +550,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti bool initAlerts(const StatsdConfig& config, const unordered_map& metricProducerMap, + const sp& anomalyAlarmMonitor, vector>& allMetricProducers, vector>& allAnomalyTrackers) { unordered_map anomalyTrackerMap; @@ -512,7 +569,7 @@ bool initAlerts(const StatsdConfig& config, } const int metricIndex = itr->second; sp metric = allMetricProducers[metricIndex]; - sp anomalyTracker = metric->addAnomalyTracker(alert); + sp anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor); if (anomalyTracker == nullptr) { // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). return false; @@ -522,6 +579,9 @@ bool initAlerts(const StatsdConfig& config, } for (int i = 0; i < config.subscription_size(); ++i) { const Subscription& subscription = config.subscription(i); + if (subscription.rule_type() != Subscription::ALERT) { + continue; + } if (subscription.subscriber_information_case() == Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { ALOGW("subscription \"%lld\" has no subscriber info.\"", @@ -540,13 +600,60 @@ bool initAlerts(const StatsdConfig& config, return true; } +bool initAlarms(const StatsdConfig& config, const ConfigKey& key, + const sp& periodicAlarmMonitor, + const long timeBaseSec, + vector>& allAlarmTrackers) { + unordered_map alarmTrackerMap; + uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC; + for (int i = 0; i < config.alarm_size(); i++) { + const Alarm& alarm = config.alarm(i); + if (alarm.offset_millis() <= 0) { + ALOGW("Alarm offset_millis should be larger than 0."); + return false; + } + if (alarm.period_millis() <= 0) { + ALOGW("Alarm period_millis should be larger than 0."); + return false; + } + alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size())); + allAlarmTrackers.push_back( + new AlarmTracker(startMillis, alarm, key, periodicAlarmMonitor)); + } + for (int i = 0; i < config.subscription_size(); ++i) { + const Subscription& subscription = config.subscription(i); + if (subscription.rule_type() != Subscription::ALARM) { + continue; + } + if (subscription.subscriber_information_case() == + Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) { + ALOGW("subscription \"%lld\" has no subscriber info.\"", + (long long)subscription.id()); + return false; + } + const auto& itr = alarmTrackerMap.find(subscription.rule_id()); + if (itr == alarmTrackerMap.end()) { + ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", + (long long)subscription.id(), (long long)subscription.rule_id()); + return false; + } + const int trackerIndex = itr->second; + allAlarmTrackers[trackerIndex]->addSubscription(subscription); + } + return true; +} + bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const UidMap& uidMap, - const long timeBaseSec, set& allTagIds, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, + const long timeBaseSec, + set& allTagIds, vector>& allAtomMatchers, vector>& allConditionTrackers, vector>& allMetricProducers, vector>& allAnomalyTrackers, + vector>& allPeriodicAlarmTrackers, unordered_map>& conditionToMetricMap, unordered_map>& trackerToMetricMap, unordered_map>& trackerToConditionMap, @@ -573,10 +680,16 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, ALOGE("initMetricProducers failed"); return false; } - if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) { + if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers, + allAnomalyTrackers)) { ALOGE("initAlerts failed"); return false; } + if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseSec, allPeriodicAlarmTrackers)) { + ALOGE("initAlarms failed"); + return false; + } + return true; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 4f19ada5b0225efb9fc324397846844af2f10807..386de0b84aa00c9a027e7ea57ea8d8032bd5f400 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef METRIC_UTIL_H -#define METRIC_UTIL_H + +#pragma once + #include #include #include #include +#include "../anomaly/AlarmTracker.h" #include "../condition/ConditionTracker.h" #include "../external/StatsPullerManagerImpl.h" #include "../matchers/LogMatchingTracker.h" +#include "../metrics/MetricProducer.h" namespace android { namespace os { @@ -93,17 +96,22 @@ bool initMetrics( // Parameters are the members of MetricsManager. See MetricsManager for declaration. bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const UidMap& uidMap, - const long timeBaseSec, std::set& allTagIds, + const sp& anomalyAlarmMonitor, + const sp& periodicAlarmMonitor, + const long timeBaseSec, + std::set& allTagIds, std::vector>& allAtomMatchers, std::vector>& allConditionTrackers, std::vector>& allMetricProducers, vector>& allAnomalyTrackers, + vector>& allPeriodicAlarmTrackers, std::unordered_map>& conditionToMetricMap, std::unordered_map>& trackerToMetricMap, std::unordered_map>& trackerToConditionMap, std::set &noReportMetricIds); +bool isStateTracker(const SimplePredicate& simplePredicate, std::vector* primaryKeys); + } // namespace statsd } // namespace os } // namespace android -#endif // METRIC_UTIL_H diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 272e90be662da6eb9395f7e34e546f16d5ce593c..3c5f5a2b9907afc0f10f383a5c91290edb94bf44 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -164,4 +164,87 @@ message ConfigMetricsReportList { optional ConfigKey config_key = 1; repeated ConfigMetricsReport reports = 2; -} \ No newline at end of file +} + +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; +} diff --git a/cmds/statsd/src/stats_log_common.proto b/cmds/statsd/src/stats_log_common.proto index aeecd23c65f4be1a1a773127292de54a4678db11..c41f31ebaa3f951f539cf951552a00aa9c82e5af 100644 --- a/cmds/statsd/src/stats_log_common.proto +++ b/cmds/statsd/src/stats_log_common.proto @@ -46,87 +46,4 @@ message UidMapping { 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 78ebe3380b8ed1c7d412d69e11db9f5a3fda4eb8..cab61e9787c6559e465d8c19a69d02c5a1ee7d07 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -70,7 +70,7 @@ void writeDimensionToProtoHelper(const std::vector& dims, size_t* in } if (depth == valueDepth && valuePrefix == prefix) { - long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + uint64_t 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()) { @@ -99,10 +99,10 @@ void writeDimensionToProtoHelper(const std::vector& dims, size_t* in (*index)++; } else if (valueDepth > depth && valuePrefix == prefix) { // Writing the sub tree - long long dimensionToken = protoOutput->start( + uint64_t 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 = + uint64_t tupleToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth), protoOutput); @@ -123,7 +123,7 @@ void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStr } 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); + uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); size_t index = 0; writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, protoOutput); protoOutput->end(topToken); @@ -189,7 +189,7 @@ void writeFieldValueTreeToStreamHelper(const std::vector& dims, size (*index)++; } else if (valueDepth > depth && valuePrefix == prefix) { // Writing the sub tree - long long msg_token = 0; + uint64_t msg_token = 0ULL; if (valueDepth == depth + 2) { msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); @@ -212,7 +212,7 @@ void writeFieldValueTreeToStreamHelper(const std::vector& dims, size void writeFieldValueTreeToStream(int tagId, const std::vector& values, util::ProtoOutputStream* protoOutput) { - long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); + uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId); size_t index = 0; writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput); @@ -257,7 +257,7 @@ int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { void writePullerStatsToStream(const std::pair& pair, util::ProtoOutputStream* protoOutput) { - long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS | + uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS | FIELD_COUNT_REPEATED); protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull); diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index c4b47dcea8a204fb5a2008e70112197f0793f187..80e46d604b2180598880f9e2066dfb60b290ab04 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -16,7 +16,6 @@ #pragma once -#include #include "HashableDimensionKey.h" #include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "logd/LogReader.h" @@ -33,7 +32,7 @@ const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey(); // Minimum bucket size in seconds const long kMinBucketSizeSec = 5 * 60; -typedef std::map> ConditionKey; +typedef std::map ConditionKey; typedef std::unordered_map DimToValMap; diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index a31385470c9fcc7fb68cd966b9e889d2abcda8d4..1e8aa12112bc7f4fc97b754cf2487edb832d2a5c 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -74,6 +74,9 @@ message FieldValueMatcher { int64 gte_int = 11; MessageMatcher matches_tuple = 12; + + StringListMatcher eq_any_string = 13; + StringListMatcher neq_all_string = 14; } } @@ -81,6 +84,10 @@ message MessageMatcher { repeated FieldValueMatcher field_value_matcher = 1; } +message StringListMatcher { + repeated string str_value = 1; +} + enum LogicalOperation { LOGICAL_OPERATION_UNSPECIFIED = 0; AND = 1; diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 6a1db72b3911b57dba0b4821a5c34ccb5bbd323d..781ecede1700e625503509eb7d360a0f8d5f7ea0 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -195,6 +195,20 @@ void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) { } } +bool StorageManager::readFileToString(const char* file, string* content) { + int fd = open(file, O_RDONLY | O_CLOEXEC); + bool res = false; + if (fd != -1) { + if (android::base::ReadFdToString(fd, content)) { + res = true; + } else { + VLOG("Failed to read file %s\n", file); + } + close(fd); + } + return res; +} + void StorageManager::readConfigFromDisk(map& configsMap) { unique_ptr dir(opendir(STATS_SERVICE_DIR), closedir); if (dir == NULL) { diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index d319674b898760eee5a536d89bdc794a70da2168..6c8ed0a967046a9b2ea5806b7778400ee77b2685 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -36,6 +36,11 @@ public: */ static void writeFile(const char* file, const void* buffer, int numBytes); + /** + * Reads the file content to the buffer. + */ + static bool readFileToString(const char* file, string* content); + /** * Deletes a single file given a file name. */ diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index d9a8fc894804e945bc99e3cb68f9e59173dd3e7c..1c18f673aeb9627268c9d9f430e5aa515de6b440 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -28,10 +28,10 @@ namespace android { namespace os { namespace statsd { -bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, +bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, const ConfigKey& configKey) { if (config.section_size() == 0) { - VLOG("The alert %lld contains zero section in config(%d,%lld)", alert.id(), + VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id, configKey.GetUid(), (long long) configKey.GetId()); return false; } @@ -39,7 +39,7 @@ bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, IncidentReportArgs incidentReport; android::os::IncidentHeaderProto header; - header.set_alert_id(alert.id()); + header.set_alert_id(rule_id); header.mutable_config_key()->set_uid(configKey.GetUid()); header.mutable_config_key()->set_id(configKey.GetId()); incidentReport.addHeader(header); diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h index 229ed778af3aba9f1294f98545cabc46df00d172..1b83fe23de8f5e2ff971bb82ab7422254afc942d 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.h +++ b/cmds/statsd/src/subscriber/IncidentdReporter.h @@ -26,7 +26,7 @@ namespace statsd { /** * Calls incidentd to trigger an incident report and put in dropbox for uploading. */ -bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert, +bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id, const ConfigKey& configKey); } // namespace statsd diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp similarity index 70% rename from cmds/statsd/tests/AnomalyMonitor_test.cpp rename to cmds/statsd/tests/AlarmMonitor_test.cpp index 920ca08ef9558bb3c94d9ff96178c9fad5295385..1fccb35eccb57309806d32894a1df40c9107d717 100644 --- a/cmds/statsd/tests/AnomalyMonitor_test.cpp +++ b/cmds/statsd/tests/AlarmMonitor_test.cpp @@ -12,28 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "anomaly/AnomalyMonitor.h" +#include "anomaly/AlarmMonitor.h" #include using namespace android::os::statsd; #ifdef __ANDROID__ -TEST(AnomalyMonitor, popSoonerThan) { +TEST(AlarmMonitor, popSoonerThan) { std::string emptyMetricId; std::string emptyDimensionId; - unordered_set, SpHash> set; - AnomalyMonitor am(2); + unordered_set, SpHash> set; + AlarmMonitor am(2, [](const sp&, int64_t){}, + [](const sp&){}); set = am.popSoonerThan(5); EXPECT_TRUE(set.empty()); - sp a = new AnomalyAlarm{10}; - sp b = new AnomalyAlarm{20}; - sp c = new AnomalyAlarm{20}; - sp d = new AnomalyAlarm{30}; - sp e = new AnomalyAlarm{40}; - sp f = new AnomalyAlarm{50}; + sp a = new InternalAlarm{10}; + sp b = new InternalAlarm{20}; + sp c = new InternalAlarm{20}; + sp d = new InternalAlarm{30}; + sp e = new InternalAlarm{40}; + sp f = new InternalAlarm{50}; am.add(a); am.add(b); diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index 62bdba406de2e400db8e3f6dd3cb366ae0c355db..90c3a2f1a539bc8b0270d6eca05388f782c6f4f8 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -65,8 +65,12 @@ MATCHER_P(StatsdConfigEq, id, 0) { const int64_t testConfigId = 12345; TEST(ConfigManagerTest, TestFakeConfig) { - auto metricsManager = std::make_unique(ConfigKey(0, testConfigId), - build_fake_config(), 1000, new UidMap()); + auto metricsManager = std::make_unique( + ConfigKey(0, testConfigId), build_fake_config(), 1000, new UidMap(), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){}), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){})); EXPECT_TRUE(metricsManager->isConfigValid()); } diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 2320a9d89f6b1f4d6de7881236e1672b9c9915b5..36c6e0c964bdf4d461a116cbad21ab9e48815320 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -294,6 +294,159 @@ TEST(AtomMatcherTest, TestAttributionMatcher) { EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); } +TEST(AtomMatcherTest, TestNeqAnyStringMatcher) { + UidMap uidMap; + uidMap.updateMap( + {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, + {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), + android::String16("Pkg2"), android::String16("PkG3")} /* package name list */); + + 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"); + + AttributionNodeInternal attribution_node4; + attribution_node4.set_uid(1066); + attribution_node4.set_tag("location3"); + std::vector attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3, attribution_node4}; + + // Set up the event + LogEvent event(TAG_ID, 0); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + // Match first node. + auto attributionMatcher = simpleMatcher->add_field_value_matcher(); + attributionMatcher->set_field(FIELD_ID_1); + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_UID_FIELD_ID); + auto neqStringList = attributionMatcher->mutable_matches_tuple() + ->mutable_field_value_matcher(0) + ->mutable_neq_all_string(); + neqStringList->add_str_value("pkg2"); + neqStringList->add_str_value("pkg3"); + + auto fieldMatcher = simpleMatcher->add_field_value_matcher(); + fieldMatcher->set_field(FIELD_ID_2); + fieldMatcher->set_eq_string("some value"); + + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + neqStringList->Clear(); + neqStringList->add_str_value("pkg1"); + neqStringList->add_str_value("pkg3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::ANY); + neqStringList->Clear(); + neqStringList->add_str_value("maps.com"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + neqStringList->Clear(); + neqStringList->add_str_value("PkG3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::LAST); + neqStringList->Clear(); + neqStringList->add_str_value("AID_STATSD"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestEqAnyStringMatcher) { + UidMap uidMap; + uidMap.updateMap( + {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, + {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), + android::String16("Pkg2"), android::String16("PkG3")} /* package name list */); + + AttributionNodeInternal attribution_node1; + attribution_node1.set_uid(1067); + 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"); + + AttributionNodeInternal attribution_node4; + attribution_node4.set_uid(1066); + attribution_node4.set_tag("location3"); + std::vector attribution_nodes = {attribution_node1, attribution_node2, + attribution_node3, attribution_node4}; + + // Set up the event + LogEvent event(TAG_ID, 0); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + // Match first node. + auto attributionMatcher = simpleMatcher->add_field_value_matcher(); + attributionMatcher->set_field(FIELD_ID_1); + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_UID_FIELD_ID); + auto eqStringList = attributionMatcher->mutable_matches_tuple() + ->mutable_field_value_matcher(0) + ->mutable_eq_any_string(); + eqStringList->add_str_value("AID_ROOT"); + eqStringList->add_str_value("AID_INCIDENTD"); + + auto fieldMatcher = simpleMatcher->add_field_value_matcher(); + fieldMatcher->set_field(FIELD_ID_2); + fieldMatcher->set_eq_string("some value"); + + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::ANY); + eqStringList->Clear(); + eqStringList->add_str_value("AID_STATSD"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + eqStringList->Clear(); + eqStringList->add_str_value("pkg1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + auto normalStringField = fieldMatcher->mutable_eq_any_string(); + normalStringField->add_str_value("some value123"); + normalStringField->add_str_value("some value"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + normalStringField->Clear(); + normalStringField->add_str_value("AID_STATSD"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + eqStringList->Clear(); + eqStringList->add_str_value("maps.com"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + TEST(AtomMatcherTest, TestBoolMatcher) { UidMap uidMap; // Set up the matcher diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index f90ca409e84cffd7f88f8985ed51ad881d95f625..5d8c3f72551e038501049045f65325fc0824eb91 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -271,19 +271,25 @@ StatsdConfig buildCirclePredicates() { TEST(MetricsManagerTest, TestGoodConfig) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildGoodConfig(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); EXPECT_EQ(1u, allMetricProducers.size()); @@ -293,112 +299,148 @@ TEST(MetricsManagerTest, TestGoodConfig) { TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildDimensionMetricsWithMultiTags(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildCircleMatchers(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildMissingMatchers(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildMissingPredicate(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildCirclePredicates(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { UidMap uidMap; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; StatsdConfig config = buildAlertWithUnknownMetric(); set allTagIds; vector> allAtomMatchers; vector> allConditionTrackers; vector> allMetricProducers; std::vector> allAnomalyTrackers; + std::vector> allAlarmTrackers; unordered_map> conditionToMetricMap; unordered_map> trackerToMetricMap; unordered_map> trackerToConditionMap; std::set noReportMetricIds; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, + anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, + allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); } diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index cb72697941e0c4f340f1faaeeedba573ff5a3200..efed42e2613c271a01f67eec3f153e1d0a80e200 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -41,18 +41,27 @@ using android::util::ProtoOutputStream; */ class MockMetricsManager : public MetricsManager { public: - MockMetricsManager() : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, new UidMap()) { + MockMetricsManager() : MetricsManager( + ConfigKey(1, 12345), StatsdConfig(), 1000, + new UidMap(), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){}), + new AlarmMonitor(10, [](const sp&, int64_t){}, + [](const sp&){})) { } MOCK_METHOD0(byteSize, size_t()); - MOCK_METHOD2(onDumpReport, void(const uint64_t timeNs, ProtoOutputStream* output)); + + MOCK_METHOD1(dropData, void(const uint64_t dropTimeNs)); }; TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. - StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {}); + StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0, + [](const ConfigKey& key) {}); MockMetricsManager mockMetricsManager; @@ -67,11 +76,11 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp subscriberAlarmMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) { - broadcastCount++; - }); + StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); MockMetricsManager mockMetricsManager; @@ -93,9 +102,10 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp subscriberAlarmMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, 0, + StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); MockMetricsManager mockMetricsManager; @@ -105,7 +115,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); - EXPECT_CALL(mockMetricsManager, onDumpReport(_, _)).Times(1); + EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); // Expect to call the onDumpReport and skip the broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index f26c10d33e6731dde07686312eb83d45fc2b2f0b..ca656ed9ab354502ea8095ac73de342d5f0bb7cd 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -36,9 +36,11 @@ const string kApp2 = "app2.sharing.1"; TEST(UidMapTest, TestIsolatedUID) { sp m = new UidMap(); - sp anomalyMonitor; + sp anomalyAlarmMonitor; + sp subscriberAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. - StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {}); + StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [](const ConfigKey& key) {}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3330ee93eddf62c0302c7d3b5501d81c886249b9 --- /dev/null +++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp @@ -0,0 +1,68 @@ +// 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 "src/anomaly/AlarmTracker.h" + +#include +#include +#include + +using namespace testing; +using android::sp; +using std::set; +using std::unordered_map; +using std::vector; + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +const ConfigKey kConfigKey(0, 12345); + +TEST(AlarmTrackerTest, TestTriggerTimestamp) { + sp subscriberAlarmMonitor = + new AlarmMonitor(100, [](const sp&, int64_t){}, + [](const sp&){}); + Alarm alarm; + alarm.set_offset_millis(15 * MS_PER_SEC); + alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr + uint64_t startMillis = 100000000 * MS_PER_SEC; + AlarmTracker tracker(startMillis, alarm, kConfigKey, + subscriberAlarmMonitor); + + EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15); + + uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10; + std::unordered_set, SpHash> firedAlarmSet = + subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + EXPECT_TRUE(firedAlarmSet.empty()); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15); + + currentTimeSec = startMillis / MS_PER_SEC + 7000; + firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); + EXPECT_EQ(firedAlarmSet.size(), 1u); + tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet); + EXPECT_TRUE(firedAlarmSet.empty()); + EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15 + 2 * 60 * 60); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 3dc3fd1160f19e55d7db2d50156b79d8c5710142..e826a52c2f336cc61f11f62caaa0142eacd9cf40 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -75,10 +75,10 @@ void makeWakeLockEvent( event->init(); } -std::map> getWakeLockQueryKey( +std::map getWakeLockQueryKey( const Position position, const std::vector &uids, const string& conditionName) { - std::map> outputKeyMap; + std::map outputKeyMap; std::vector uid_indexes; int pos[] = {1, 1, 1}; int depth = 2; @@ -104,7 +104,7 @@ std::map> getWakeLockQueryKey( Value value((int32_t)uids[idx]); HashableDimensionKey dim; dim.addValue(FieldValue(field, value)); - outputKeyMap[StringToId(conditionName)].push_back(dim); + outputKeyMap[StringToId(conditionName)] = dim; } return outputKeyMap; } @@ -122,6 +122,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/, simplePredicate, trackerNameIndexMap); + EXPECT_FALSE(conditionTracker.isSliced()); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -193,6 +194,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { } TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { + std::vector> allConditions; SimplePredicate simplePredicate; simplePredicate.set_start(StringToId("SCREEN_TURNED_ON")); simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF")); @@ -205,6 +207,7 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); + EXPECT_FALSE(conditionTracker.isSliced()); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -257,14 +260,14 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); - // result should still be true EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); EXPECT_TRUE(changedCache[0]); } TEST(SimpleConditionTrackerTest, TestSlicedCondition) { + std::vector> allConditions; for (Position position : - { Position::ANY, Position::FIRST, Position::LAST}) { + { Position::FIRST, Position::LAST}) { vector dimensionInCondition; std::unordered_set dimensionKeys; @@ -281,6 +284,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); + std::vector uids = {111, 222, 333}; LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -305,12 +309,20 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); } EXPECT_TRUE(changedCache[0]); + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), uids.size()); + } // Now test query const auto queryKey = getWakeLockQueryKey(position, uids, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + false, false, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -331,6 +343,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } else { EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); } + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + // wake lock 1 release LogEvent event3(1 /*tagId*/, 0 /*timestamp*/); @@ -350,6 +365,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } else { EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); } + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); LogEvent event4(1 /*tagId*/, 0 /*timestamp*/); makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it. @@ -362,18 +379,26 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { changedCache); EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); EXPECT_TRUE(changedCache[0]); + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), uids.size()); + } // query again conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + false, false, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - } } TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { + std::vector> allConditions; vector dimensionInCondition; std::unordered_set dimensionKeys; @@ -391,6 +416,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); + EXPECT_FALSE(conditionTracker.isSliced()); + std::vector uid_list1 = {111, 1111, 11111}; string uid1_wl1 = "wl1_1"; std::vector uid_list2 = {222, 2222, 22222}; @@ -419,6 +446,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + true, true, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -463,13 +491,15 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { conditionCache[0] = ConditionState::kNotEvaluated; dimensionKeys.clear(); conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + true, true, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } TEST(SimpleConditionTrackerTest, TestStopAll) { + std::vector> allConditions; for (Position position : - {Position::ANY, Position::FIRST, Position::LAST}) { + { Position::FIRST, Position::LAST }) { vector dimensionInCondition; std::unordered_set dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( @@ -510,12 +540,23 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size()); } EXPECT_TRUE(changedCache[0]); + { + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uid_list1.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } + } // Now test query const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + false, false, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -538,11 +579,23 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { conditionTracker.mSlicedConditionState.size()); } EXPECT_TRUE(changedCache[0]); + { + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uid_list2.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } + } + // TEST QUERY const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + false, false, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -561,11 +614,22 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { changedCache); EXPECT_TRUE(changedCache[0]); EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uid_list1.size() + uid_list2.size(), + conditionTracker.getChangedToFalseDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } + } // TEST QUERY const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + false, false, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); @@ -573,6 +637,7 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + false, false, conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } diff --git a/cmds/statsd/tests/condition/StateTracker_test.cpp b/cmds/statsd/tests/condition/StateTracker_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a66254afce0cc9107421cfa02da31ef884b54eb --- /dev/null +++ b/cmds/statsd/tests/condition/StateTracker_test.cpp @@ -0,0 +1,112 @@ +// 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 "src/condition/StateTracker.h" +#include "tests/statsd_test_util.h" + +#include +#include +#include +#include +#include + +using std::map; +using std::unordered_map; +using std::vector; + +#ifdef __ANDROID__ +namespace android { +namespace os { +namespace statsd { + +const int kUidProcTag = 27; + +SimplePredicate getUidProcStatePredicate() { + SimplePredicate simplePredicate; + simplePredicate.set_start(StringToId("UidProcState")); + + simplePredicate.mutable_dimensions()->set_field(kUidProcTag); + simplePredicate.mutable_dimensions()->add_child()->set_field(1); + simplePredicate.mutable_dimensions()->add_child()->set_field(2); + + simplePredicate.set_count_nesting(false); + return simplePredicate; +} + +void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) { + event->write(uid); + event->write(state); + event->init(); +} + +TEST(StateTrackerTest, TestStateChange) { + int uid1 = 111; + int uid2 = 222; + + int state1 = 1001; + int state2 = 1002; + unordered_map trackerNameIndexMap; + trackerNameIndexMap[StringToId("UidProcState")] = 0; + vector primaryFields; + primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1)); + StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(), + trackerNameIndexMap, primaryFields); + + LogEvent event(kUidProcTag, 0 /*timestamp*/); + makeUidProcStateEvent(uid1, state1, &event); + + vector matcherState; + matcherState.push_back(MatchingState::kMatched); + vector> allPredicates; + vector conditionCache(1, ConditionState::kNotEvaluated); + vector changedCache(1, false); + + tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_TRUE(changedCache[0]); + + changedCache[0] = false; + conditionCache[0] = ConditionState::kNotEvaluated; + tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(0ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_FALSE(changedCache[0]); + + LogEvent event2(kUidProcTag, 0 /*timestamp*/); + makeUidProcStateEvent(uid1, state2, &event2); + + changedCache[0] = false; + conditionCache[0] = ConditionState::kNotEvaluated; + tracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(1ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_TRUE(changedCache[0]); + + LogEvent event3(kUidProcTag, 0 /*timestamp*/); + makeUidProcStateEvent(uid2, state1, &event3); + changedCache[0] = false; + conditionCache[0] = ConditionState::kNotEvaluated; + tracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_TRUE(changedCache[0]); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a08f6067c96c27d05e51cada655830baac53bde5 --- /dev/null +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp @@ -0,0 +1,879 @@ +// 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 "src/StatsLogProcessor.h" +#include "src/stats_log_util.h" +#include "tests/statsd_test_util.h" + +#include + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +namespace { + +StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition( + DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); + dimensions->add_child()->set_field(2); // job name field. + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); + if (addExtraDimensionInCondition) { + syncDimension->add_child()->set_field(2 /* name field*/); + } + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(StringToId("CombinationPredicate")); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); + auto dimensionWhat = metric->mutable_dimensions_in_what(); + dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); + dimensionWhat->add_child()->set_field(2); // job name field. + *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) { + for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) { + for (auto aggregationType : {DurationMetric::MAX_SPARSE, DurationMetric::SUM}) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition( + aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension); + int64_t bucketStartTimeNs = 10000000000; + int64_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()); + + 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> events; + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 11)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 40)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 102)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 450)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 650)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 100)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + bucketSizeNs + 640)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 650)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 10)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 200)); + 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(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 401)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + + 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); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).duration_metrics(), &metrics); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job0"); // job name + 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).duration_nanos(), 40 - 11); + 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().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job1"); // job name + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10); + 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); + + data = metrics.data(2); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); + 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(3); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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(), 450 - 401 + bucketSizeNs - 600); + 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 + 650 - 640); + 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); + } else { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job0"); // job name + 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).duration_nanos(), 40 - 11); + 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().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job1"); // job name + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10); + 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); + + data = metrics.data(2); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100); + 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(3); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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(), 450 - 401); + 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(), bucketSizeNs - 600 + 110); + 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); + } + } + } +} + +namespace { + +StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition( + DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + *dimensions = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + dimensions->add_child()->set_field(2); // job name field. + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + if (addExtraDimensionInCondition) { + syncDimension->add_child()->set_field(2 /* name field*/); + } + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(StringToId("CombinationPredicate")); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); + *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition) { + for (bool isFullLink : {true, false}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition( + aggregationType, !isFullLink); + int64_t bucketStartTimeNs = 10000000000; + int64_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()); + + 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 attributions3 = { + CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector> events; + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 55)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 120)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 121)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 450)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 501)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 100)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500)); + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", + bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back( + CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back( + CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 110)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 300)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc", + bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 550)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 800)); + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs + 700)); + + 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); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).duration_metrics(), &metrics); + + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(metrics.data_size(), 3); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1); + 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_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(metrics.data_size(), 3); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 450 - 300); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101); + 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 { + +StatsdConfig CreateDurationMetricConfig_PartialLink_AND_CombinationCondition( + DurationMetric::AggregationType aggregationType) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + *dimensions = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + dimensions->add_child()->set_field(2); // job name field. + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + syncDimension->add_child()->set_field(2 /* name field*/); + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(StringToId("CombinationPredicate")); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); + *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *metric->mutable_dimensions_in_condition() = *syncDimension; + + + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = + CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(aggregationType); + int64_t bucketStartTimeNs = 10000000000; + int64_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()); + + 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 attributions3 = { + CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector> events; + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 55)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 120)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 121)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 450)); + + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, + bucketStartTimeNs + 501)); + events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + bucketSizeNs + 100)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500)); + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", + bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back( + CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back( + CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 110)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 300)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc", + bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 550)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 800)); + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs + 700)); + + 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); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).duration_metrics(), &metrics); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + 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(0).duration_nanos(), bucketSizeNs - 1 - 600 + 50); + + data = metrics.data(2); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); + 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(3); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1); + 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_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 50); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 450 - 300); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100); + 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(3); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101); + 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); + } + } +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp similarity index 86% rename from cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp rename to cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp index 01348bd664aa81c0bc4f1a237d0c81251d3babdd..435e1991b9e36402ef928ef3a9256cf54c5825f8 100644 --- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp @@ -28,7 +28,7 @@ namespace statsd { namespace { -StatsdConfig CreateCountMetricWithNoLinkConfig() { +StatsdConfig CreateCountMetric_NoLink_CombinationCondition_Config() { StatsdConfig config; auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher(); *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher; @@ -67,9 +67,9 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() { } // namespace -TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { +TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition) { ConfigKey cfgKey; - auto config = CreateCountMetricWithNoLinkConfig(); + auto config = CreateCountMetric_NoLink_CombinationCondition_Config(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; @@ -227,7 +227,7 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { namespace { -StatsdConfig CreateCountMetricWithLinkConfig() { +StatsdConfig CreateCountMetric_Link_CombinationCondition() { StatsdConfig config; auto appCrashMatcher = CreateProcessCrashAtomMatcher(); *config.add_atom_matcher() = appCrashMatcher; @@ -274,9 +274,9 @@ StatsdConfig CreateCountMetricWithLinkConfig() { } // namespace -TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { +TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition) { ConfigKey cfgKey; - auto config = CreateCountMetricWithLinkConfig(); + auto config = CreateCountMetric_Link_CombinationCondition(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; @@ -413,7 +413,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { namespace { -StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) { +StatsdConfig CreateDurationMetricConfig_NoLink_CombinationCondition( + DurationMetric::AggregationType aggregationType) { StatsdConfig config; *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); @@ -445,6 +446,7 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag metric->set_id(StringToId("BatterySaverModeDurationMetric")); metric->set_what(inBatterySaverModePredicate.id()); metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( android::util::SYNC_STATE_CHANGED, {Position::FIRST}); return config; @@ -452,10 +454,10 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag } // namespace -TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { - for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { +TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition) { + for (auto aggregationType : { DurationMetric::MAX_SPARSE}) { // DurationMetric::SUM, ConfigKey cfgKey; - auto config = CreateDurationMetricConfigNoLink(aggregationType); + auto config = CreateDurationMetricConfig_NoLink_CombinationCondition(aggregationType); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; @@ -529,43 +531,77 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { auto data = metrics.data(0); EXPECT_FALSE(data.dimensions_in_what().has_field()); 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_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_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); + 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_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); + 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_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"); EXPECT_EQ(data.bucket_info_size(), 2); - EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); + + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300); + } else { + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 300); + } 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_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 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"); EXPECT_EQ(data.bucket_info_size(), 2); - EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); + } else { + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs + 700 - 600); + } 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_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 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); } } namespace { -StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) { +StatsdConfig CreateDurationMetricConfig_Link_CombinationCondition( + DurationMetric::AggregationType aggregationType) { StatsdConfig config; *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); @@ -599,6 +635,7 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType metric->set_id(StringToId("AppInBackgroundMetric")); metric->set_what(isInBackgroundPredicate.id()); metric->set_condition(combinationPredicate->id()); + metric->set_aggregation_type(aggregationType); *metric->mutable_dimensions_in_what() = CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( @@ -617,10 +654,10 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType } // namespace -TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { +TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition) { for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { ConfigKey cfgKey; - auto config = CreateDurationMetricConfigWithLink(aggregationType); + auto config = CreateDurationMetricConfig_Link_CombinationCondition(aggregationType); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; @@ -701,26 +738,50 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { 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"); - 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_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_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); + 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).start_bucket_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 100 - 201); + 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); + } 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"); - 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_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_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); + 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).start_bucket_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 299); + 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); + } } } diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75ceafb489b3ec7b6537098c206876926987dde0 --- /dev/null +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp @@ -0,0 +1,799 @@ +// 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 "src/StatsLogProcessor.h" +#include "src/stats_log_util.h" +#include "tests/statsd_test_util.h" + +#include + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +namespace { + +StatsdConfig CreateDurationMetricConfig_NoLink_SimpleCondition( + DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); + dimensions->add_child()->set_field(2); // job name field. + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED, + {Position::FIRST}); + if (addExtraDimensionInCondition) { + syncDimension->add_child()->set_field(2 /* name field*/); + } + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = isSyncingPredicate; + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(isSyncingPredicate.id()); + metric->set_aggregation_type(aggregationType); + auto dimensionWhat = metric->mutable_dimensions_in_what(); + dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED); + dimensionWhat->add_child()->set_field(2); // job name field. + *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) { + for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : {true, false}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfig_NoLink_SimpleCondition( + aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension); + int64_t bucketStartTimeNs = 10000000000; + int64_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()); + + 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> events; + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 1)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 10)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 200)); + 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(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 401)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + + 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); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).duration_metrics(), &metrics); + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job0"); // job name + 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).duration_nanos(), 40); + 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().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job1"); // job name + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100); + 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); + + data = metrics.data(2); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300); + 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(3); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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_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_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job0"); // job name + 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).duration_nanos(), 40); + 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().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job1"); // job name + ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(), + android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100); + 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); + + data = metrics.data(2); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 300); + 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(3); + EXPECT_EQ(data.dimensions_in_what().field(), + android::util::SCHEDULED_JOB_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), + 2); // job name field + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(), + "job2"); // job name + 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 ); + 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(), bucketSizeNs - 600 + 700); + 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); + } + } + } +} + +namespace { + +StatsdConfig createDurationMetric_Link_SimpleConditionConfig( + DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + *dimensions = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + dimensions->add_child()->set_field(2); // job name field. + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + if (addExtraDimensionInCondition) { + syncDimension->add_child()->set_field(2 /* name field*/); + } + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = isSyncingPredicate; + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(isSyncingPredicate.id()); + metric->set_aggregation_type(aggregationType); + *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) { + for (bool isFullLink : {true, false}) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = createDurationMetric_Link_SimpleConditionConfig( + aggregationType, !isFullLink); + int64_t bucketStartTimeNs = 10000000000; + int64_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()); + + 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 attributions3 = { + CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector> events; + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500)); + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600)); + events.push_back( + CreateFinishScheduledJobEvent({CreateAttribution(333, "App2")}, "job2", + bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back( + CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back( + CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 110)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 300)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc", + bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 550)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 800)); + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs + 700)); + + 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); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).duration_metrics(), &metrics); + + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(metrics.data_size(), 3); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1); + 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_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(metrics.data_size(), 3); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 500 - 300); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701); + 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 { + +StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig( + DurationMetric::AggregationType aggregationType) { + StatsdConfig config; + *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + auto scheduledJobPredicate = CreateScheduledJobPredicate(); + auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions(); + *dimensions = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + dimensions->add_child()->set_field(2); // job name field. + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + syncDimension->add_child()->set_field(2 /* name field*/); + + *config.add_predicate() = scheduledJobPredicate; + *config.add_predicate() = isSyncingPredicate; + + auto metric = config.add_duration_metric(); + metric->set_bucket(FIVE_MINUTES); + metric->set_id(StringToId("scheduledJob")); + metric->set_what(scheduledJobPredicate.id()); + metric->set_condition(isSyncingPredicate.id()); + metric->set_aggregation_type(aggregationType); + *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *metric->mutable_dimensions_in_condition() = *syncDimension; + + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions( + android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition) { + for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = createDurationMetric_PartialLink_SimpleConditionConfig( + aggregationType); + int64_t bucketStartTimeNs = 10000000000; + int64_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()); + + 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 attributions3 = { + CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector> events; + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101)); + + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500)); + events.push_back(CreateStartScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600)); + events.push_back(CreateFinishScheduledJobEvent( + {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back( + CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back( + CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", + bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 50)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + 110)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", + bucketStartTimeNs + 300)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc", + bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 550)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + 800)); + events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", + bucketStartTimeNs + bucketSizeNs + 700)); + + 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); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).duration_metrics(), &metrics); + + if (aggregationType == DurationMetric::SUM) { + EXPECT_EQ(4, metrics.data_size()); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + 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(0).duration_nanos(), bucketSizeNs - 1 - 400 - 100); + + data = metrics.data(2); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); + 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(3); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1); + 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_elapsed_nanos(), + bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + } else { + EXPECT_EQ(metrics.data_size(), 4); + auto data = metrics.data(0); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50); + 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); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 100); + 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.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600); + + data = metrics.data(2); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333); + EXPECT_EQ("ReadEmail", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 2); + 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(0).duration_nanos(), 500 - 300); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700); + 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(3); + ValidateAttributionUidDimension( + data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444); + ValidateAttributionUidDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444); + EXPECT_EQ("ReadDoc", + data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701); + 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); + } + } +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp index 7d9c8a8208eef8d19110965e64bba72856d62344..fc6e42010710c8738072761526dc30b7846f930d 100644 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -34,7 +34,7 @@ using testing::Contains; * Test merge isolated and host uid */ -int uidAtomTagId = android::util::CPU_TIME_PER_UID_FREQ; +int uidAtomTagId = android::util::CPU_CLUSTER_TIME; int nonUidAtomTagId = android::util::SYSTEM_UPTIME; int timestamp = 1234; int isolatedUid = 30; diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index bd114439b83c31a2a5729c97573160072a4b8b5a..5c4eda8348d104c46c7f4d8d0842152f895e55ff 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -277,21 +277,20 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); const auto& configStats = stats.mConfigStats[key]; - int maxCount = StatsdStats::kMaxTimestampCount; - EXPECT_EQ(maxCount, configStats.broadcast_sent_time_sec_size()); - EXPECT_EQ(maxCount, configStats.data_drop_time_sec_size()); - EXPECT_EQ(maxCount, configStats.dump_report_time_sec_size()); + size_t maxCount = StatsdStats::kMaxTimestampCount; + EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); + EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size()); + EXPECT_EQ(maxCount, configStats->dump_report_time_sec.size()); // the oldest timestamp is the second timestamp in history - EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0)); - EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0)); - EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0)); + EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); // the last timestamp is the newest timestamp. - EXPECT_EQ(newTimestamp, - configStats.broadcast_sent_time_sec(StatsdStats::kMaxTimestampCount - 1)); - EXPECT_EQ(newTimestamp, configStats.data_drop_time_sec(StatsdStats::kMaxTimestampCount - 1)); - EXPECT_EQ(newTimestamp, configStats.dump_report_time_sec(StatsdStats::kMaxTimestampCount - 1)); + EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->dump_report_time_sec.back()); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 20ddbe9f0e38e8d5ca13fedb6f843b4d43ede3cc..a07683e5b0455fa7c39371ee9f911586710a4acd 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -175,9 +175,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { {getMockedDimensionKey(conditionTagId, 2, "222")}; sp wizard = new NaggyMock(); - EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse)); + EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse)); - EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue)); + EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue)); CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, bucketStartTimeNs); @@ -201,6 +201,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { } TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { + sp alarmMonitor; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; @@ -222,7 +223,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { bucketStartTimeNs); countProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = countProducer.addAnomalyTracker(alert); + sp anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); // Bucket is flushed yet. @@ -315,6 +316,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { } TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { + sp alarmMonitor; Alert alert; alert.set_id(11); alert.set_metric_id(1); @@ -337,7 +339,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { bucketStartTimeNs); countProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = countProducer.addAnomalyTracker(alert); + sp anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 79695967a6ddc9eeb9a35b7a7647067abe10bf1c..1b22d75da7b4499f7d2c4b51102f7d80056a4bd0 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -239,6 +239,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { } TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { + sp alarmMonitor; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; @@ -263,7 +264,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); durationProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = durationProducer.addAnomalyTracker(alert); + sp anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); LogEvent start_event(tagId, startTimeNs); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 8246268d88e78e84c0e497f10a116ce121e64955..23d31717d99dcaafe669b526be34f18fa41734c4 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -113,9 +113,9 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")}; sp wizard = new NaggyMock(); - EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse)); + EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse)); - EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue)); + EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue)); EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 0eb8ce2603bdc32ee022f897c850902a2e0e3f6f..77b3ace90aff9cfd4b5d1ca0bb7d4a0968db62dd 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -129,6 +129,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { } TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { + sp alarmMonitor; GaugeMetric metric; metric.set_id(metricId); metric.set_bucket(ONE_MINUTE); @@ -145,8 +146,9 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { 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); + sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); shared_ptr event1 = make_shared(tagId, bucketStartTimeNs + 10); @@ -339,6 +341,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { } TEST(GaugeMetricProducerTest, TestAnomalyDetection) { + sp alarmMonitor; sp wizard = new NaggyMock(); shared_ptr pullerManager = @@ -363,7 +366,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { alert.set_num_buckets(2); const int32_t refPeriodSec = 60; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert); + sp anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); int tagId = 1; std::shared_ptr event1 = std::make_shared(tagId, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index a164c12134b592878c23df6ede896b8dd2663118..57a8925a122e3db3ef488e80a7d5faaf73c4500c 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -42,14 +42,13 @@ 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 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"); - const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); @@ -66,7 +65,7 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, {}); + false, false, {}); tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey()); // Event starts again. This would not change anything as it already starts. @@ -86,7 +85,6 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { TEST(MaxDurationTrackerTest, TestStopAll) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); @@ -103,7 +101,7 @@ TEST(MaxDurationTrackerTest, TestStopAll) { int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, {}); + false, false, {}); tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey()); @@ -124,7 +122,6 @@ TEST(MaxDurationTrackerTest, TestStopAll) { TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -140,7 +137,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, {}); + false, false, {}); // The event starts. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); @@ -166,7 +163,6 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -182,7 +178,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - false, {}); + false, false, {}); // 2 starts tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); @@ -204,14 +200,14 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { } TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { - const std::vector conditionKey = {key1}; + const HashableDimensionKey conditionDimKey = key1; vector dimensionInCondition; sp wizard = new NaggyMock(); ConditionKey conditionKey1; MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 1, "1"); - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; + conditionKey1[StringToId("APP_BACKGROUND")] = conditionDimKey; /** Start in first bucket, stop in second bucket. Condition turns on and off in the first bucket @@ -229,7 +225,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true, - {}); + false, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); @@ -250,8 +246,6 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { - const std::vector conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; - vector dimensionInCondition; sp wizard = new NaggyMock(); @@ -276,13 +270,15 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { alert.set_num_buckets(2); const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, {anomalyTracker}); + true, false, {anomalyTracker}); tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); - sp alarm = anomalyTracker->mAlarms.begin()->second; + 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. @@ -299,8 +295,6 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { // 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")}; - vector dimensionInCondition; sp wizard = new NaggyMock(); @@ -308,7 +302,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; ConditionKey conditionKey2; - conditionKey2[StringToId("APP_BACKGROUND")] = {getMockedDimensionKey(TagId, 4, "2")}; + conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2"); unordered_map> buckets; @@ -336,10 +330,12 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { alert.set_num_buckets(2); const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, {anomalyTracker}); + true, false, {anomalyTracker}); tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1); tracker.noteConditionChanged(key1, true, conditionStarts1); @@ -356,8 +352,6 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { // 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")}; - vector dimensionInCondition; sp wizard = new NaggyMock(); @@ -365,7 +359,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; ConditionKey conditionKey2; - conditionKey2[StringToId("APP_BACKGROUND")] = {getMockedDimensionKey(TagId, 4, "2")}; + conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2"); unordered_map> buckets; @@ -390,10 +384,12 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { alert.set_num_buckets(2); const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, - true, {anomalyTracker}); + true, false, {anomalyTracker}); tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1); tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); @@ -409,4 +405,4 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { } // namespace android #else GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif +#endif \ No newline at end of file diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index cb731c555e908c56e5fb051660e56192366e085a..54abcb28a44926b01ec66a84e756ad090c54993b 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -40,7 +40,7 @@ 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 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; @@ -48,8 +48,6 @@ const uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; TEST(OringDurationTrackerTest, TestDurationOverlap) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -65,7 +63,7 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, false, {}); + bucketSizeNs, false, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -83,8 +81,6 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { TEST(OringDurationTrackerTest, TestDurationNested) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -99,7 +95,7 @@ TEST(OringDurationTrackerTest, TestDurationNested) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, false, {}); + bucketSizeNs, false, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl @@ -132,7 +128,7 @@ TEST(OringDurationTrackerTest, TestStopAll) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, false, {}); + bucketSizeNs, false, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl @@ -148,8 +144,6 @@ TEST(OringDurationTrackerTest, TestStopAll) { TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -165,7 +159,7 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, false, {}); + bucketSizeNs, false, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -189,8 +183,6 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { TEST(OringDurationTrackerTest, TestDurationConditionChange) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -199,7 +191,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { ConditionKey key1; key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4 + EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) // #4 .WillOnce(Return(ConditionState::kFalse)); unordered_map> buckets; @@ -212,7 +204,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, {}); + bucketSizeNs, true, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -229,8 +221,6 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { TEST(OringDurationTrackerTest, TestDurationConditionChange2) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -239,7 +229,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { ConditionKey key1; key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - EXPECT_CALL(*wizard, query(_, key1, _, _)) + EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) .Times(2) .WillOnce(Return(ConditionState::kFalse)) .WillOnce(Return(ConditionState::kTrue)); @@ -254,7 +244,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, {}); + bucketSizeNs, true, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); // condition to false; record duration 5n @@ -273,8 +263,6 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -283,7 +271,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { ConditionKey key1; key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4 + EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) // #4 .WillOnce(Return(ConditionState::kFalse)); unordered_map> buckets; @@ -295,7 +283,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, {}); + bucketSizeNs, true, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1); @@ -315,8 +303,6 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -334,10 +320,12 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, true, {anomalyTracker}); + bucketSizeNs, true, false, {anomalyTracker}); // Nothing in the past bucket. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); @@ -384,7 +372,6 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -403,10 +390,12 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { uint64_t bucketNum = 0; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs, - bucketSizeNs, false, {anomalyTracker}); + bucketSizeNs, false, false, {anomalyTracker}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false); @@ -433,8 +422,6 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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"); vector dimensionInCondition; @@ -453,14 +440,16 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); + sp alarmMonitor; + sp anomalyTracker = + new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs, - bucketSizeNs, false, {anomalyTracker}); + bucketSizeNs, false, false, {anomalyTracker}); tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1 EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); - sp alarm = anomalyTracker->mAlarms.begin()->second; + sp alarm = anomalyTracker->mAlarms.begin()->second; EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC)); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); @@ -487,7 +476,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U); // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time. - std::unordered_set, SpHash> firedAlarms({alarm}); + std::unordered_set, SpHash> firedAlarms({alarm}); anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms); EXPECT_EQ(0u, anomalyTracker->mAlarms.size()); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec); @@ -502,4 +491,4 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { } // namespace android #else GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif +#endif \ No newline at end of file diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index ce4fa327849318240e055f7fb4906066544041ec..a0addcccb7a1d12f418746a1e892df5a7f009679 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -346,6 +346,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { } TEST(ValueMetricProducerTest, TestAnomalyDetection) { + sp alarmMonitor; Alert alert; alert.set_id(101); alert.set_metric_id(metricId); @@ -365,7 +366,7 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { -1 /*not pulled*/, bucketStartTimeNs); valueProducer.setBucketSize(60 * NS_PER_SEC); - sp anomalyTracker = valueProducer.addAnomalyTracker(alert); + sp anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor); shared_ptr event1 diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index a01de6334ab4fab7a799361695bf26b2b86a2ee2..f040bf9f37aea819cf7fa321f1c74af4cb9d668d 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -26,9 +26,10 @@ namespace statsd { class MockConditionWizard : public ConditionWizard { public: - MOCK_METHOD4(query, + MOCK_METHOD6(query, ConditionState(const int conditionIndex, const ConditionKey& conditionParameters, const vector& dimensionFields, + const bool isSubsetDim, const bool isPartialLink, std::unordered_set* dimensionKeySet)); }; diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 7568348108c40e7f5813d678cf9b7731b20c5811..2678c8a93463c32dff73f36614ae0c2168c44d12 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -26,6 +26,28 @@ AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { return atom_matcher; } +AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name, + ScheduledJobStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(3); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateStartScheduledJobAtomMatcher() { + return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart", + ScheduledJobStateChanged::STARTED); +} + +AtomMatcher CreateFinishScheduledJobAtomMatcher() { + return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish", + ScheduledJobStateChanged::FINISHED); +} + AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { AtomMatcher atom_matcher; atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); @@ -168,6 +190,14 @@ AtomMatcher CreateProcessCrashAtomMatcher() { "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED); } +Predicate CreateScheduledJobPredicate() { + Predicate predicate; + predicate.set_id(StringToId("ScheduledJobRunningPredicate")); + predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart")); + predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish")); + return predicate; +} + Predicate CreateBatterySaverModePredicate() { Predicate predicate; predicate.set_id(StringToId("BatterySaverIsOn")); @@ -290,6 +320,32 @@ std::unique_ptr CreateScreenBrightnessChangedEvent( } +std::unique_ptr CreateScheduledJobStateChangedEvent( + const std::vector& attributions, const string& jobName, + const ScheduledJobStateChanged::State state, uint64_t timestampNs) { + auto event = std::make_unique(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs); + event->write(attributions); + event->write(jobName); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr CreateStartScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs) { + return CreateScheduledJobStateChangedEvent( + attributions, name, ScheduledJobStateChanged::STARTED, timestampNs); +} + +// Create log event when scheduled job finishes. +std::unique_ptr CreateFinishScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs) { + return CreateScheduledJobStateChangedEvent( + attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs); +} + std::unique_ptr CreateWakelockStateChangedEvent( const std::vector& attributions, const string& wakelockName, const WakelockStateChanged::State state, uint64_t timestampNs) { @@ -391,9 +447,10 @@ std::unique_ptr CreateIsolatedUidChangedEvent( sp CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, const ConfigKey& key) { sp uidMap = new UidMap(); - sp anomalyMonitor = new AnomalyMonitor(10); // 10 seconds + sp anomalyAlarmMonitor; + sp periodicAlarmMonitor; sp processor = new StatsLogProcessor( - uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){}); + uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){}); processor->OnConfigUpdated(key, config); return processor; } @@ -418,7 +475,6 @@ int64_t StringToId(const string& str) { void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) { EXPECT_EQ(value.field(), atomId); - EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1); // Attribution field. EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1); // Uid only. diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 1708cc31bf14f8ce7447787ee2eeaf31897895c7..14eba1f67d9d141785ba7d478f57f9f0712e15cc 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -28,6 +28,15 @@ namespace statsd { // Create AtomMatcher proto to simply match a specific atom type. AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); +// Create AtomMatcher proto for scheduled job state changed. +AtomMatcher CreateScheduledJobStateChangedAtomMatcher(); + +// Create AtomMatcher proto for starting a scheduled job. +AtomMatcher CreateStartScheduledJobAtomMatcher(); + +// Create AtomMatcher proto for a scheduled job is done. +AtomMatcher CreateFinishScheduledJobAtomMatcher(); + // Create AtomMatcher proto for screen brightness state changed. AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); @@ -73,6 +82,9 @@ Predicate CreateScreenIsOnPredicate(); // Create Predicate proto for screen is off. Predicate CreateScreenIsOffPredicate(); +// Create Predicate proto for a running scheduled job. +Predicate CreateScheduledJobPredicate(); + // Create Predicate proto for battery saver mode. Predicate CreateBatterySaverModePredicate(); @@ -107,6 +119,16 @@ std::unique_ptr CreateScreenStateChangedEvent( std::unique_ptr CreateScreenBrightnessChangedEvent( int level, uint64_t timestampNs); +// Create log event when scheduled job starts. +std::unique_ptr CreateStartScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs); + +// Create log event when scheduled job finishes. +std::unique_ptr CreateFinishScheduledJobEvent( + const std::vector& attributions, + const string& name, uint64_t timestampNs); + // Create log event when battery saver starts. std::unique_ptr CreateBatterySaverOnEvent(uint64_t timestampNs); // Create log event when battery saver stops. diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index c7e4c7b717937c50cd92e8e387c15f8bc54a5791..baf235bb91befdb9607fc154537d4b07e875c605 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_PACKAGE_NAME := StatsdDogfood +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk index 091f184ad3046ffc4c4d865fe08ab5ed74b17063..219cd9525bbd202be0983f0263a69ba6764cda25 100644 --- a/cmds/statsd/tools/loadtest/Android.mk +++ b/cmds/statsd/tools/loadtest/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_PACKAGE_NAME := StatsdLoadtest +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java index a12def0db9fb71139be1a190a6d08ae88fc3c640..bed4d980969230bf48aa7441be7b671301888756 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java @@ -50,7 +50,7 @@ import android.widget.Toast; import com.android.os.StatsLog.ConfigMetricsReport; import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.os.StatsLogCommon.StatsdStatsReport; +import com.android.os.StatsLog.StatsdStatsReport; import com.android.internal.os.StatsdConfigProto.TimeUnit; import java.util.ArrayList; diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java index 58cbcd89581e59ea08a41862f4fd145e3d267a86..3939e7e0b2fad4218ec9f5fb33f0e0d7abb5d0c2 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java @@ -16,7 +16,7 @@ package com.android.statsd.loadtest; import android.content.Context; -import com.android.os.StatsLogCommon.StatsdStatsReport; +import com.android.os.StatsLog.StatsdStatsReport; import com.android.internal.os.StatsdConfigProto.TimeUnit; public class StatsdStatsRecorder extends PerfDataRecorder { diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 752b662e050dc5f53dda5c1e5ebb38e6a403efa2..1a1d431b1d2dbd01ceec1775e9882ed21f92ed0c 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -3,19 +3,12 @@ Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landro Landroid/accounts/IAccountManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/animation/LayoutTransition;->cancel()V Landroid/animation/ValueAnimator;->animateValue(F)V -Landroid/animation/ValueAnimator;->getDurationScale()F Landroid/animation/ValueAnimator;->sDurationScale:F -Landroid/animation/ValueAnimator;->setDurationScale(F)V -Landroid/app/Activity;->convertFromTranslucent()V -Landroid/app/Activity;->convertToTranslucent(Landroid/app/Activity$TranslucentConversionListener;Landroid/app/ActivityOptions;)Z Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions; Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder; Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo; -Landroid/app/ActivityManager;->addOnUidImportanceListener(Landroid/app/ActivityManager$OnUidImportanceListener;I)V Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z -Landroid/app/ActivityManager;->forceStopPackage(Ljava/lang/String;)V -Landroid/app/ActivityManager;->getCurrentUser()I -Landroid/app/ActivityManager;->getPackageImportance(Ljava/lang/String;)I +Landroid/app/ActivityManager;->getMaxRecentTasksStatic()I Landroid/app/ActivityManager;->getService()Landroid/app/IActivityManager; Landroid/app/ActivityManager;->IActivityManagerSingleton:Landroid/util/Singleton; Landroid/app/ActivityManager;->isLowRamDeviceStatic()Z @@ -25,7 +18,6 @@ Landroid/app/ActivityManagerNative;->asInterface(Landroid/os/IBinder;)Landroid/a Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager; Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J -Landroid/app/ActivityManager;->removeOnUidImportanceListener(Landroid/app/ActivityManager$OnUidImportanceListener;)V Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I Landroid/app/Activity;->mApplication:Landroid/app/Application; @@ -33,6 +25,7 @@ Landroid/app/Activity;->mComponent:Landroid/content/ComponentName; Landroid/app/Activity;->mFragments:Landroid/app/FragmentController; Landroid/app/Activity;->mHandler:Landroid/os/Handler; Landroid/app/Activity;->mInstrumentation:Landroid/app/Instrumentation; +Landroid/app/Activity;->mMainThread:Landroid/app/ActivityThread; Landroid/app/Activity;->mReferrer:Ljava/lang/String; Landroid/app/Activity;->mResultCode:I Landroid/app/Activity;->mResultData:Landroid/content/Intent; @@ -76,6 +69,7 @@ Landroid/app/ActivityThread;->getPackageInfoNoCheck(Landroid/content/pm/Applicat Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager; Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String; Landroid/app/ActivityThread;->getSystemContext()Landroid/app/ContextImpl; +Landroid/app/ActivityThread;->handleBindApplication(Landroid/app/ActivityThread$AppBindData;)V Landroid/app/ActivityThread$H;->BIND_SERVICE:I Landroid/app/ActivityThread$H;->CREATE_SERVICE:I Landroid/app/ActivityThread$H;->DUMP_PROVIDER:I @@ -97,6 +91,7 @@ Landroid/app/ActivityThread;->mH:Landroid/app/ActivityThread$H; Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application; Landroid/app/ActivityThread;->mInstrumentation:Landroid/app/Instrumentation; Landroid/app/ActivityThread;->mLocalProvidersByName:Landroid/util/ArrayMap; +Landroid/app/ActivityThread;->mLocalProviders:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->mNumVisibleActivities:I Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->mProviderMap:Landroid/util/ArrayMap; @@ -115,14 +110,8 @@ Landroid/app/ActivityThread$ServiceArgsData;->args:Landroid/content/Intent; Landroid/app/ActivityThread$ServiceArgsData;->token:Landroid/os/IBinder; Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager; Landroid/app/ActivityThread;->startActivityNow(Landroid/app/Activity;Ljava/lang/String;Landroid/content/Intent;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/Activity$NonConfigurationInstances;)Landroid/app/Activity; -Landroid/app/admin/DevicePolicyManager;->getDeviceOwnerComponentOnAnyUser()Landroid/content/ComponentName; -Landroid/app/admin/DevicePolicyManager;->getDeviceOwner()Ljava/lang/String; -Landroid/app/admin/DevicePolicyManager;->getProfileOwner()Landroid/content/ComponentName; Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List; -Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(J)V -Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(JZ)V Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;I)Z -Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;)Z Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;ZI)V Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;Z)V Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_packageHasActiveAdmins:I @@ -133,8 +122,6 @@ Landroid/app/AlarmManager;->FLAG_IDLE_UNTIL:I Landroid/app/AlarmManager;->FLAG_STANDALONE:I Landroid/app/AlarmManager;->FLAG_WAKE_FROM_IDLE:I Landroid/app/AlarmManager;->mService:Landroid/app/IAlarmManager; -Landroid/app/AlarmManager;->set(IJJJLandroid/app/AlarmManager$OnAlarmListener;Landroid/os/Handler;Landroid/os/WorkSource;)V -Landroid/app/AlarmManager;->set(IJJJLandroid/app/PendingIntent;Landroid/os/WorkSource;)V Landroid/app/AlarmManager;->WINDOW_EXACT:J Landroid/app/AlarmManager;->WINDOW_HEURISTIC:J Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams; @@ -158,8 +145,6 @@ Landroid/app/ApplicationPackageManager;->configurationChanged()V Landroid/app/ApplicationPackageManager;->deletePackage(Ljava/lang/String;Landroid/content/pm/IPackageDeleteObserver;I)V Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/String;ILandroid/content/pm/IPackageStatsObserver;)V Landroid/app/ApplicationPackageManager;->(Landroid/app/ContextImpl;Landroid/content/pm/IPackageManager;)V -Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;)I -Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;I)I Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager; Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I @@ -172,46 +157,20 @@ Landroid/app/AppOpsManager;->OP_GET_USAGE_STATS:I Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I Landroid/app/AppOpsManager;->OP_READ_PHONE_STATE:I Landroid/app/AppOpsManager;->OP_READ_SMS:I -Landroid/app/AppOpsManager;->OP_SYSTEM_ALERT_WINDOW:I Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I Landroid/app/AppOpsManager;->OP_WRITE_SMS:I Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I -Landroid/app/AppOpsManager;->setMode(IILjava/lang/String;I)V Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I Landroid/app/backup/BackupDataInput$EntityHeader;->dataSize:I Landroid/app/backup/BackupDataInput$EntityHeader;->key:Ljava/lang/String; -Landroid/app/backup/BackupDataInput;->(Ljava/io/FileDescriptor;)V Landroid/app/backup/BackupDataInputStream;->dataSize:I Landroid/app/backup/BackupDataInputStream;->key:Ljava/lang/String; -Landroid/app/backup/BackupDataOutput;->(Ljava/io/FileDescriptor;)V Landroid/app/backup/BackupDataOutput;->mBackupWriter:J Landroid/app/backup/BackupHelperDispatcher$Header;->chunkSize:I Landroid/app/backup/BackupHelperDispatcher$Header;->keyPrefix:Ljava/lang/String; -Landroid/app/backup/BackupManager;->backupNow()V -Landroid/app/backup/BackupManager;->beginRestoreSession()Landroid/app/backup/RestoreSession; -Landroid/app/backup/BackupManager;->cancelBackups()V -Landroid/app/backup/BackupManager;->getAvailableRestoreToken(Ljava/lang/String;)J -Landroid/app/backup/BackupManager;->getCurrentTransport()Ljava/lang/String; -Landroid/app/backup/BackupManager;->isBackupEnabled()Z -Landroid/app/backup/BackupManager;->listAllTransports()[Ljava/lang/String; -Landroid/app/backup/BackupManagerMonitor;->()V -Landroid/app/backup/BackupManager;->requestBackup([Ljava/lang/String;Landroid/app/backup/BackupObserver;Landroid/app/backup/BackupManagerMonitor;I)I -Landroid/app/backup/BackupManager;->selectBackupTransport(Landroid/content/ComponentName;Landroid/app/backup/SelectBackupTransportCallback;)V -Landroid/app/backup/BackupManager;->selectBackupTransport(Ljava/lang/String;)Ljava/lang/String; -Landroid/app/backup/BackupManager;->setAutoRestore(Z)V -Landroid/app/backup/BackupManager;->setBackupEnabled(Z)V -Landroid/app/backup/BackupManager;->updateTransportAttributes(Landroid/content/ComponentName;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;)V -Landroid/app/backup/BackupObserver;->()V -Landroid/app/backup/BackupTransport;->()V Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I Landroid/app/backup/FullBackupDataOutput;->addSize(J)V Landroid/app/backup/FullBackupDataOutput;->mData:Landroid/app/backup/BackupDataOutput; -Landroid/app/backup/RestoreDescription;->(Ljava/lang/String;I)V -Landroid/app/backup/RestoreSession;->endRestoreSession()V -Landroid/app/backup/RestoreSession;->getAvailableRestoreSets(Landroid/app/backup/RestoreObserver;)I -Landroid/app/backup/RestoreSession;->restoreAll(JLandroid/app/backup/RestoreObserver;)I -Landroid/app/backup/RestoreSet;->(Ljava/lang/String;Ljava/lang/String;J)V -Landroid/app/backup/SelectBackupTransportCallback;->()V Landroid/app/ContentProviderHolder;->info:Landroid/content/pm/ProviderInfo; Landroid/app/ContentProviderHolder;->(Landroid/content/pm/ProviderInfo;)V Landroid/app/ContentProviderHolder;->provider:Landroid/content/IContentProvider; @@ -243,7 +202,9 @@ Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity; Landroid/app/Dialog;->mShowMessage:Landroid/os/Message; Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri; Landroid/app/FragmentManagerImpl;->mAdded:Ljava/util/ArrayList; +Landroid/app/FragmentManagerImpl;->noteStateNotSaved()V Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl; +Landroid/app/Fragment;->mWho:Ljava/lang/String; Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V @@ -267,8 +228,6 @@ Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V Landroid/app/INotificationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/INotificationManager; Landroid/app/INotificationManager$Stub$Proxy;->(Landroid/os/IBinder;)V -Landroid/app/InstantAppResolverService;->()V -Landroid/app/InstantAppResolverService$InstantAppResolutionCallback;->onInstantAppResolveInfo(Ljava/util/List;)V Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Landroid/app/Activity;Landroid/content/Intent;ILandroid/os/Bundle;)Landroid/app/Instrumentation$ActivityResult; Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;)Landroid/app/Instrumentation$ActivityResult; Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/Instrumentation$ActivityResult; @@ -308,11 +267,11 @@ Landroid/app/NativeActivity;->setWindowFormat(I)V Landroid/app/NativeActivity;->showIme(I)V Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList; Landroid/app/Notification$Builder;->setChannel(Ljava/lang/String;)Landroid/app/Notification$Builder; -Landroid/app/Notification;->EXTRA_SUBSTITUTE_APP_NAME:Ljava/lang/String; Landroid/app/Notification;->isGroupSummary()Z Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager; Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V Landroid/app/NotificationManager;->sService:Landroid/app/INotificationManager; +Landroid/app/Notification;->mGroupKey:Ljava/lang/String; Landroid/app/Notification;->mLargeIcon:Landroid/graphics/drawable/Icon; Landroid/app/Notification;->mSmallIcon:Landroid/graphics/drawable/Icon; Landroid/app/Notification;->setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V @@ -343,12 +302,10 @@ Landroid/app/StatusBarManager;->expandNotificationsPanel()V Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V Landroid/app/StatusBarManager;->expandSettingsPanel()V Landroid/app/StatusBarManager;->getService()Lcom/android/internal/statusbar/IStatusBarService; +Landroid/app/TaskStackListener;->()V Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker; Landroid/app/trust/ITrustManager$Stub$Proxy;->(Landroid/os/IBinder;)V -Landroid/app/usage/UsageStatsManager;->getAppStandbyBuckets()Ljava/util/Map; Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager; -Landroid/app/usage/UsageStatsManager;->setAppStandbyBuckets(Ljava/util/Map;)V -Landroid/app/usage/UsageStatsManager;->whitelistAppTemporarily(Ljava/lang/String;JLandroid/os/UserHandle;)V Landroid/app/usage/UsageStats;->mLastEvent:I Landroid/app/WallpaperColors;->getColorHints()I Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap; @@ -360,37 +317,26 @@ Landroid/app/WallpaperManager;->sGlobals:Landroid/app/WallpaperManager$Globals; Landroid/appwidget/AppWidgetManager;->bindAppWidgetIdIfAllowed(IILandroid/content/ComponentName;Landroid/os/Bundle;)Z Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;Landroid/os/Bundle;)V Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V +Landroid/appwidget/AppWidgetManager;->mService:Lcom/android/internal/appwidget/IAppWidgetService; Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z -Landroid/bluetooth/BluetoothAdapter;->disableBLE()Z Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z -Landroid/bluetooth/BluetoothAdapter;->enableBLE()Z -Landroid/bluetooth/BluetoothAdapter;->enableNoAutoConnect()Z Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I -Landroid/bluetooth/BluetoothAdapter;->isBleScanAlwaysAvailable()Z -Landroid/bluetooth/BluetoothAdapter;->isLeEnabled()Z Landroid/bluetooth/BluetoothAdapter;->mService:Landroid/bluetooth/IBluetooth; Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z -Landroid/bluetooth/BluetoothDevice;->cancelBondProcess()Z Landroid/bluetooth/BluetoothDevice;->createBond(I)Z Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String; -Landroid/bluetooth/BluetoothDevice;->isConnected()Z -Landroid/bluetooth/BluetoothDevice;->isEncrypted()Z -Landroid/bluetooth/BluetoothDevice;->removeBond()Z -Landroid/bluetooth/BluetoothDevice;->setPhonebookAccessPermission(I)Z Landroid/bluetooth/BluetoothGattCharacteristic;->mInstance:I Landroid/bluetooth/BluetoothGattCharacteristic;->mService:Landroid/bluetooth/BluetoothGattService; Landroid/bluetooth/BluetoothGattDescriptor;->mCharacteristic:Landroid/bluetooth/BluetoothGattCharacteristic; Landroid/bluetooth/BluetoothGattDescriptor;->mInstance:I Landroid/bluetooth/BluetoothGatt;->refresh()Z Landroid/bluetooth/BluetoothHeadset;->close()V -Landroid/bluetooth/BluetoothHeadset;->setPriority(Landroid/bluetooth/BluetoothDevice;I)Z Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid; Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String; +Landroid/bluetooth/IBluetoothManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth; Landroid/bluetooth/IBluetooth$Stub$Proxy;->getAddress()Ljava/lang/String; -Landroid/bluetooth/le/BluetoothLeScanner;->startScanFromSource(Ljava/util/List;Landroid/bluetooth/le/ScanSettings;Landroid/os/WorkSource;Landroid/bluetooth/le/ScanCallback;)V -Landroid/bluetooth/le/ScanSettings$Builder;->setScanResultType(I)Landroid/bluetooth/le/ScanSettings$Builder; Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor; Landroid/content/BroadcastReceiver$PendingResult;->(ILjava/lang/String;Landroid/os/Bundle;IZZLandroid/os/IBinder;II)V Landroid/content/BroadcastReceiver;->setPendingResult(Landroid/content/BroadcastReceiver$PendingResult;)V @@ -412,23 +358,17 @@ Landroid/content/ContentResolver;->mContext:Landroid/content/Context; Landroid/content/ContentResolver;->mPackageName:Ljava/lang/String; Landroid/content/ContentValues;->(Ljava/util/HashMap;)V Landroid/content/ContentValues;->mValues:Ljava/util/HashMap; -Landroid/content/Context;->bindServiceAsUser(Landroid/content/Intent;Landroid/content/ServiceConnection;ILandroid/os/UserHandle;)Z -Landroid/content/Context;->createCredentialProtectedStorageContext()Landroid/content/Context; -Landroid/content/Context;->createPackageContextAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)Landroid/content/Context; Landroid/content/Context;->getSharedPrefsFile(Ljava/lang/String;)Ljava/io/File; Landroid/content/Context;->getThemeResId()I -Landroid/content/Context;->isCredentialProtectedStorage()Z -Landroid/content/Context;->PERSISTENT_DATA_BLOCK_SERVICE:Ljava/lang/String; Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;I)V -Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;Landroid/os/Bundle;)V Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/os/Bundle;Landroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V -Landroid/content/ContextWrapper;->createCredentialProtectedStorageContext()Landroid/content/Context; Landroid/content/ContextWrapper;->getDisplay()Landroid/view/Display; Landroid/content/ContextWrapper;->mBase:Landroid/content/Context; Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal; Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver; Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard; +Landroid/content/IClipboard$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/content/IContentProvider;->call(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle; Landroid/content/IContentService;->cancelSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/content/ComponentName;)V Landroid/content/IContentService;->getMasterSyncAutomatically()Z @@ -437,25 +377,17 @@ Landroid/content/IContentService$Stub;->asInterface(Landroid/os/IBinder;)Landroi Landroid/content/IContentService$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String; Landroid/content/IntentFilter;->mActions:Ljava/util/ArrayList; -Landroid/content/IntentFilter;->setOrder(I)V Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent; +Landroid/content/pm/ActivityInfo;->resizeMode:I Landroid/content/pm/ApplicationInfo;->enabledSetting:I Landroid/content/pm/ApplicationInfo;->getBaseResourcePath()Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->installLocation:I Landroid/content/pm/ApplicationInfo;->isForwardLocked()Z -Landroid/content/pm/ApplicationInfo;->isInstantApp()Z -Landroid/content/pm/ApplicationInfo;->isPrivilegedApp()Z Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->privateFlags:I Landroid/content/pm/ApplicationInfo;->scanPublicSourceDir:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->secondaryNativeLibraryDir:Ljava/lang/String; -Landroid/content/pm/ApplicationInfo;->targetSandboxVersion:I -Landroid/content/pm/InstantAppIntentFilter;->(Ljava/lang/String;Ljava/util/List;)V -Landroid/content/pm/InstantAppResolveInfo;->getPackageName()Ljava/lang/String; -Landroid/content/pm/InstantAppResolveInfo;->(Landroid/content/pm/InstantAppResolveInfo$InstantAppDigest;Ljava/lang/String;Ljava/util/List;I)V -Landroid/content/pm/InstantAppResolveInfo$InstantAppDigest;->getDigestPrefix()[I -Landroid/content/pm/InstantAppResolveInfo$InstantAppDigest;->(Ljava/lang/String;)V Landroid/content/pm/IPackageManager;->getInstallLocation()I Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo; Landroid/content/pm/IPackageManager;->setApplicationEnabledSetting(Ljava/lang/String;IIILjava/lang/String;)V @@ -469,10 +401,6 @@ Landroid/content/pm/IPackageStatsObserver$Stub;->()V Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo; Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager; Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V -Landroid/content/pm/PackageInstaller$SessionParams;->setAllocateAggressive(Z)V -Landroid/content/pm/PackageInstaller$SessionParams;->setGrantedRuntimePermissions([Ljava/lang/String;)V -Landroid/content/pm/PackageInstaller$SessionParams;->setInstallAsInstantApp(Z)V -Landroid/content/pm/PackageManager;->addOnPermissionsChangeListener(Landroid/content/pm/PackageManager$OnPermissionsChangedListener;)V Landroid/content/pm/PackageManager;->buildRequestPermissionsIntent([Ljava/lang/String;)Landroid/content/Intent; Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V @@ -486,8 +414,6 @@ Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)I Landroid/content/pm/PackageManager;->NO_NATIVE_LIBRARIES:I Landroid/content/pm/PackageManager;->queryBroadcastReceivers(Landroid/content/Intent;II)Ljava/util/List; -Landroid/content/pm/PackageManager;->removeOnPermissionsChangeListener(Landroid/content/pm/PackageManager$OnPermissionsChangedListener;)V -Landroid/content/pm/PackageManager;->verifyIntentFilter(IILjava/util/List;)V Landroid/content/pm/PackageParser$Activity;->info:Landroid/content/pm/ActivityInfo; Landroid/content/pm/PackageParser$ActivityIntentInfo;->activity:Landroid/content/pm/PackageParser$Activity; Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V @@ -526,41 +452,22 @@ Landroid/content/res/AssetFileDescriptor;->mLength:J Landroid/content/res/AssetFileDescriptor;->mStartOffset:J Landroid/content/res/AssetManager;->addAssetPathAsSharedLibrary(Ljava/lang/String;)I Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I -Landroid/content/res/AssetManager;->addAssetPathNative(Ljava/lang/String;Z)I -Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I -Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V -Landroid/content/res/AssetManager;->ensureStringBlocks()[Landroid/content/res/StringBlock; -Landroid/content/res/AssetManager;->getArraySize(I)I Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; -Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; -Landroid/content/res/AssetManager;->getNativeStringBlock(I)J Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; Landroid/content/res/AssetManager;->getResourceEntryName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I Landroid/content/res/AssetManager;->getResourceName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourcePackageName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceTypeName(I)Ljava/lang/String; -Landroid/content/res/AssetManager;->getStringBlockCount()I Landroid/content/res/AssetManager;->()V Landroid/content/res/AssetManager;->isUpToDate()Z -Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I Landroid/content/res/AssetManager;->mObject:J -Landroid/content/res/AssetManager;->mStringBlocks:[Landroid/content/res/StringBlock; -Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; -Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J -Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z -Landroid/content/res/AssetManager;->retrieveArray(I[I)I -Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V -Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I -Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/ColorStateList$ColorStateListFactory;->(Landroid/content/res/ColorStateList;)V Landroid/content/res/ColorStateList;->mColors:[I Landroid/content/res/ColorStateList;->mDefaultColor:I @@ -585,6 +492,7 @@ Landroid/content/res/ResourcesKey;->mSplitResDirs:[Ljava/lang/String; Landroid/content/res/Resources;->loadXmlResourceParser(ILjava/lang/String;)Landroid/content/res/XmlResourceParser; Landroid/content/res/Resources;->loadXmlResourceParser(Ljava/lang/String;IILjava/lang/String;)Landroid/content/res/XmlResourceParser; Landroid/content/res/Resources;->mResourcesImpl:Landroid/content/res/ResourcesImpl; +Landroid/content/res/Resources;->mSystem:Landroid/content/res/Resources; Landroid/content/res/Resources;->mTmpValue:Landroid/util/TypedValue; Landroid/content/res/Resources;->mTypedArrayPool:Landroid/util/Pools$SynchronizedPool; Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V @@ -602,9 +510,9 @@ Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; -Landroid/content/res/XmlBlock;->close()V Landroid/content/res/XmlBlock;->([B)V Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; +Landroid/content/res/XmlBlock$Parser;->mBlock:Landroid/content/res/XmlBlock; Landroid/content/res/XmlBlock$Parser;->mParseState:J Landroid/content/SyncStatusInfo;->lastSuccessTime:J Landroid/database/AbstractCursor;->mExtras:Landroid/os/Bundle; @@ -645,26 +553,48 @@ Landroid/graphics/Camera;->native_instance:J Landroid/graphics/Canvas;->(J)V Landroid/graphics/Canvas;->release()V Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V +Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray; +Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mTransitions:Landroid/util/LongSparseLongArray; +Landroid/graphics/drawable/AnimatedStateListDrawable;->mState:Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState; Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;->callOnFinished(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I +Landroid/graphics/drawable/BitmapDrawable;->getOpticalInsets()Landroid/graphics/Insets; Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList; Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode; Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect; Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets; Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState; +Landroid/graphics/drawable/Drawable;->getOpticalInsets()Landroid/graphics/Insets; Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference; Landroid/graphics/drawable/GradientDrawable$GradientState;->mAngle:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradientColors:[I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradient:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mHeight:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mInnerRadius:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mInnerRadiusRatio:F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mOrientation:Landroid/graphics/drawable/GradientDrawable$Orientation; Landroid/graphics/drawable/GradientDrawable$GradientState;->mPadding:Landroid/graphics/Rect; Landroid/graphics/drawable/GradientDrawable$GradientState;->mPositions:[F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadiusArray:[F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadius:F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mShape:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashGap:F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashWidth:F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeWidth:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mThickness:I +Landroid/graphics/drawable/GradientDrawable$GradientState;->mThicknessRatio:F +Landroid/graphics/drawable/GradientDrawable$GradientState;->mWidth:I Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect; -Landroid/graphics/drawable/Icon;->getResPackage()Ljava/lang/String; +Landroid/graphics/drawable/Icon;->mType:I +Landroid/graphics/drawable/InsetDrawable;->mState:Landroid/graphics/drawable/InsetDrawable$InsetState; Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState; Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch; Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I Landroid/graphics/drawable/StateListDrawable;->getStateCount()I Landroid/graphics/drawable/StateListDrawable;->getStateDrawable(I)Landroid/graphics/drawable/Drawable; +Landroid/graphics/drawable/StateListDrawable;->getStateDrawableIndex([I)I Landroid/graphics/drawable/StateListDrawable;->getStateSet(I)[I Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState; Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V @@ -695,6 +625,7 @@ Landroid/graphics/SurfaceTexture;->mProducer:J Landroid/graphics/SurfaceTexture;->mSurfaceTexture:J Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I Landroid/graphics/SurfaceTexture;->postEventFromNative(Ljava/lang/ref/WeakReference;)V +Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;Ljava/lang/String;II)Landroid/graphics/Typeface; Landroid/graphics/Typeface;->mStyle:I Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface; Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V @@ -707,51 +638,15 @@ Landroid/hardware/Camera;->addCallbackBuffer([BI)V Landroid/hardware/Camera;->mNativeContext:J Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera; Landroid/hardware/Camera;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V -Landroid/hardware/display/DisplayManager;->getStableDisplaySize()Landroid/graphics/Point; Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay; Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay; Landroid/hardware/HardwareBuffer;->(J)V Landroid/hardware/HardwareBuffer;->mNativeObject:J Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager; +Landroid/hardware/input/IInputManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/hardware/input/InputManager;->getInstance()Landroid/hardware/input/InputManager; Landroid/hardware/input/InputManager;->mIm:Landroid/hardware/input/IInputManager; -Landroid/hardware/location/ContextHubInfo;->getId()I -Landroid/hardware/location/ContextHubInfo;->getMaxPacketLengthBytes()I -Landroid/hardware/location/ContextHubManager$Callback;->()V -Landroid/hardware/location/ContextHubManager;->findNanoAppOnHub(ILandroid/hardware/location/NanoAppFilter;)[I -Landroid/hardware/location/ContextHubManager;->getContextHubHandles()[I -Landroid/hardware/location/ContextHubManager;->getContextHubInfo(I)Landroid/hardware/location/ContextHubInfo; -Landroid/hardware/location/ContextHubManager;->getNanoAppInstanceInfo(I)Landroid/hardware/location/NanoAppInstanceInfo; -Landroid/hardware/location/ContextHubManager;->loadNanoApp(ILandroid/hardware/location/NanoApp;)I -Landroid/hardware/location/ContextHubManager;->registerCallback(Landroid/hardware/location/ContextHubManager$Callback;)I -Landroid/hardware/location/ContextHubManager;->sendMessage(IILandroid/hardware/location/ContextHubMessage;)I -Landroid/hardware/location/ContextHubManager;->unloadNanoApp(I)I -Landroid/hardware/location/ContextHubMessage;->getData()[B -Landroid/hardware/location/ContextHubMessage;->getMsgType()I -Landroid/hardware/location/ContextHubMessage;->getVersion()I -Landroid/hardware/location/ContextHubMessage;->(II[B)V -Landroid/hardware/location/GeofenceHardware;->addGeofence(IILandroid/hardware/location/GeofenceHardwareRequest;Landroid/hardware/location/GeofenceHardwareCallback;)Z -Landroid/hardware/location/GeofenceHardwareCallback;->()V -Landroid/hardware/location/GeofenceHardwareCallback;->onGeofenceAdd(II)V -Landroid/hardware/location/GeofenceHardwareCallback;->onGeofenceRemove(II)V -Landroid/hardware/location/GeofenceHardwareCallback;->onGeofenceTransition(IILandroid/location/Location;JI)V -Landroid/hardware/location/GeofenceHardware;->getMonitoringTypes()[I -Landroid/hardware/location/GeofenceHardware;->getStatusOfMonitoringType(I)I -Landroid/hardware/location/GeofenceHardwareMonitorCallback;->()V -Landroid/hardware/location/GeofenceHardwareMonitorCallback;->onMonitoringSystemChange(IZLandroid/location/Location;)V -Landroid/hardware/location/GeofenceHardware;->registerForMonitorStateChangeCallback(ILandroid/hardware/location/GeofenceHardwareMonitorCallback;)Z -Landroid/hardware/location/GeofenceHardware;->removeGeofence(II)Z -Landroid/hardware/location/GeofenceHardwareRequest;->createCircularGeofence(DDD)Landroid/hardware/location/GeofenceHardwareRequest; -Landroid/hardware/location/GeofenceHardwareRequest;->setLastTransition(I)V -Landroid/hardware/location/GeofenceHardwareRequest;->setMonitorTransitions(I)V -Landroid/hardware/location/GeofenceHardwareRequest;->setNotificationResponsiveness(I)V -Landroid/hardware/location/GeofenceHardwareRequest;->setUnknownTimer(I)V -Landroid/hardware/location/NanoAppFilter;->(JIIJ)V -Landroid/hardware/location/NanoApp;->(J[B)V -Landroid/hardware/location/NanoAppInstanceInfo;->getAppId()J -Landroid/hardware/location/NanoAppInstanceInfo;->getAppVersion()I -Landroid/hardware/location/NanoAppInstanceInfo;->getHandle()I -Landroid/hardware/location/NanoAppInstanceInfo;->getName()Ljava/lang/String; +Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;->()V Landroid/hardware/SerialPort;->mNativeContext:I Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;->confidenceLevel:I Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;->(II)V @@ -784,6 +679,7 @@ Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;->vendorUuid:Ljava/util/U Landroid/hardware/SystemSensorManager$BaseEventQueue;->dispatchAdditionalInfoEvent(III[F[I)V Landroid/hardware/SystemSensorManager$BaseEventQueue;->dispatchFlushCompleteEvent(I)V Landroid/hardware/SystemSensorManager$BaseEventQueue;->dispatchSensorEvent(I[FIJ)V +Landroid/hardware/usb/IUsbManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/hardware/usb/UsbDeviceConnection;->mNativeContext:J Landroid/hardware/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V Landroid/hardware/usb/UsbRequest;->mNativeContext:J @@ -819,20 +715,18 @@ Landroid/icu/util/Calendar;->writeObject(Ljava/io/ObjectOutputStream;)V Landroid/icu/util/ChineseCalendar;->readObject(Ljava/io/ObjectInputStream;)V Landroid/icu/util/IslamicCalendar;->readObject(Ljava/io/ObjectInputStream;)V Landroid/icu/util/SimpleTimeZone;->readObject(Ljava/io/ObjectInputStream;)V +Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText; Landroid/location/CountryDetector;->detectCountry()Landroid/location/Country; Landroid/location/Country;->getCountryIso()Ljava/lang/String; Landroid/location/Country;->getSource()I Landroid/location/GeocoderParams;->getClientPackage()Ljava/lang/String; Landroid/location/GeocoderParams;->getLocale()Ljava/util/Locale; +Landroid/location/IFusedProvider$Stub;->()V +Landroid/location/IGeocodeProvider$Stub;->()V +Landroid/location/IGeofenceProvider$Stub;->()V +Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager; Landroid/location/ILocationManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/location/LocationManager;->mService:Landroid/location/ILocationManager; -Landroid/location/LocationManager;->requestLocationUpdates(Landroid/location/LocationRequest;Landroid/location/LocationListener;Landroid/os/Looper;)V -Landroid/location/LocationRequest;->createFromDeprecatedProvider(Ljava/lang/String;JFZ)Landroid/location/LocationRequest; -Landroid/location/LocationRequest;->setHideFromAppOps(Z)V -Landroid/location/LocationRequest;->setWorkSource(Landroid/os/WorkSource;)V -Landroid/location/Location;->setIsFromMockProvider(Z)V -Landroid/Manifest$permission;->REAL_GET_TASKS:Ljava/lang/String; -Landroid/media/AudioAttributes$Builder;->setInternalCapturePreset(I)Landroid/media/AudioAttributes$Builder; Landroid/media/AudioAttributes;->mContentType:I Landroid/media/AudioAttributes;->mFlags:I Landroid/media/AudioAttributes;->mFormattedTags:Ljava/lang/String; @@ -840,9 +734,6 @@ Landroid/media/AudioAttributes;->mSource:I Landroid/media/AudioAttributes;->mUsage:I Landroid/media/AudioDevicePortConfig;->(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V Landroid/media/AudioDevicePort;->(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V -Landroid/media/AudioFocusInfo;->getClientId()Ljava/lang/String; -Landroid/media/AudioFocusInfo;->getClientUid()I -Landroid/media/AudioFocusInfo;->getLossReceived()I Landroid/media/AudioFormat;->(IIII)V Landroid/media/AudioFormat;->mChannelMask:I Landroid/media/AudioFormat;->mEncoding:I @@ -857,32 +748,21 @@ Landroid/media/AudioGainConfig;->mValues:[I Landroid/media/AudioGain;->(IIIIIIIII)V Landroid/media/AudioHandle;->(I)V Landroid/media/AudioHandle;->mId:I -Landroid/media/AudioManager;->abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;)I +Landroid/media/AudioManager;->forceVolumeControlStream(I)V +Landroid/media/AudioManager;->getOutputLatency(I)I Landroid/media/AudioManager;->getService()Landroid/media/IAudioService; Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap; -Landroid/media/AudioManager;->registerAudioPolicy(Landroid/media/audiopolicy/AudioPolicy;)I -Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioFocusRequest;Landroid/media/audiopolicy/AudioPolicy;)I -Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;II)I -Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;IILandroid/media/audiopolicy/AudioPolicy;)I Landroid/media/AudioManager;->setMasterMute(ZI)V Landroid/media/AudioManager;->STREAM_BLUETOOTH_SCO:I Landroid/media/AudioManager;->STREAM_SYSTEM_ENFORCED:I Landroid/media/AudioManager;->STREAM_TTS:I -Landroid/media/AudioManager;->unregisterAudioPolicyAsync(Landroid/media/audiopolicy/AudioPolicy;)V Landroid/media/AudioMixPortConfig;->(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V Landroid/media/AudioMixPort;->(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V Landroid/media/AudioPatch;->(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V Landroid/media/AudioPatch;->mHandle:Landroid/media/AudioHandle; -Landroid/media/audiopolicy/AudioMix$Builder;->build()Landroid/media/audiopolicy/AudioMix; -Landroid/media/audiopolicy/AudioMix$Builder;->(Landroid/media/audiopolicy/AudioMixingRule;)V -Landroid/media/audiopolicy/AudioMix$Builder;->setFormat(Landroid/media/AudioFormat;)Landroid/media/audiopolicy/AudioMix$Builder; -Landroid/media/audiopolicy/AudioMix$Builder;->setRouteFlags(I)Landroid/media/audiopolicy/AudioMix$Builder; Landroid/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion;->mAttr:Landroid/media/AudioAttributes; Landroid/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion;->mIntProp:I Landroid/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion;->mRule:I -Landroid/media/audiopolicy/AudioMixingRule$Builder;->addRule(Landroid/media/AudioAttributes;I)Landroid/media/audiopolicy/AudioMixingRule$Builder; -Landroid/media/audiopolicy/AudioMixingRule$Builder;->build()Landroid/media/audiopolicy/AudioMixingRule; -Landroid/media/audiopolicy/AudioMixingRule$Builder;->()V Landroid/media/audiopolicy/AudioMixingRule;->mCriteria:Ljava/util/ArrayList; Landroid/media/audiopolicy/AudioMix;->mCallbackFlags:I Landroid/media/audiopolicy/AudioMix;->mDeviceAddress:Ljava/lang/String; @@ -891,13 +771,6 @@ Landroid/media/audiopolicy/AudioMix;->mFormat:Landroid/media/AudioFormat; Landroid/media/audiopolicy/AudioMix;->mMixType:I Landroid/media/audiopolicy/AudioMix;->mRouteFlags:I Landroid/media/audiopolicy/AudioMix;->mRule:Landroid/media/audiopolicy/AudioMixingRule; -Landroid/media/audiopolicy/AudioPolicy$AudioPolicyFocusListener;->()V -Landroid/media/audiopolicy/AudioPolicy$Builder;->addMix(Landroid/media/audiopolicy/AudioMix;)Landroid/media/audiopolicy/AudioPolicy$Builder; -Landroid/media/audiopolicy/AudioPolicy$Builder;->build()Landroid/media/audiopolicy/AudioPolicy; -Landroid/media/audiopolicy/AudioPolicy$Builder;->(Landroid/content/Context;)V -Landroid/media/audiopolicy/AudioPolicy$Builder;->setAudioPolicyFocusListener(Landroid/media/audiopolicy/AudioPolicy$AudioPolicyFocusListener;)V -Landroid/media/audiopolicy/AudioPolicy$Builder;->setLooper(Landroid/os/Looper;)Landroid/media/audiopolicy/AudioPolicy$Builder; -Landroid/media/audiopolicy/AudioPolicy;->createAudioRecordSink(Landroid/media/audiopolicy/AudioMix;)Landroid/media/AudioRecord; Landroid/media/AudioPortConfig;->(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V Landroid/media/AudioPortConfig;->mChannelMask:I Landroid/media/AudioPortConfig;->mConfigMask:I @@ -928,6 +801,7 @@ Landroid/media/AudioTrack;->mNativeTrackInJavaObj:J Landroid/media/AudioTrack;->mStreamType:I Landroid/media/AudioTrack;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService; +Landroid/media/IAudioService$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V @@ -942,6 +816,8 @@ Landroid/media/MediaFile;->LAST_AUDIO_FILE_TYPE:I Landroid/media/MediaFile$MediaFileType;->fileType:I Landroid/media/MediaFile$MediaFileType;->mimeType:Ljava/lang/String; Landroid/media/MediaFile;->sFileTypeMap:Ljava/util/HashMap; +Landroid/media/MediaFormat;->getMap()Ljava/util/Map; +Landroid/media/MediaHTTPService;->createHttpServiceBinderIfNecessary(Ljava/lang/String;)Landroid/os/IBinder; Landroid/media/MediaMetadataRetriever;->getEmbeddedPicture(I)[B Landroid/media/MediaPlayer;->getMetadata(ZZ)Landroid/media/Metadata; Landroid/media/MediaPlayer;->invoke(Landroid/os/Parcel;Landroid/os/Parcel;)V @@ -951,7 +827,6 @@ Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;[Ljava/lang/String; Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)V Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;)V Landroid/media/MediaPlayer;->setRetransmitEndpoint(Ljava/net/InetSocketAddress;)V -Landroid/media/MediaRecorder$AudioSource;->RADIO_TUNER:I Landroid/media/MediaRouter$RouteInfo;->getStatusCode()I Landroid/media/MediaRouter$RouteInfo;->STATUS_CONNECTING:I Landroid/media/MediaRouter;->selectRouteInt(ILandroid/media/MediaRouter$RouteInfo;Z)V @@ -977,9 +852,9 @@ Landroid/media/RemoteDisplay;->notifyDisplayConnected(Landroid/view/Surface;IIII Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V Landroid/media/RemoteDisplay;->notifyDisplayError(I)V Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone; -Landroid/media/Ringtone;->setLooping(Z)V -Landroid/media/Ringtone;->setVolume(F)V Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper; +Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I +Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler; Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap; Landroid/media/ToneGenerator;->mNativeContext:J @@ -998,19 +873,6 @@ Landroid/media/VolumeShaper$Operation;->mXOffset:F Landroid/media/VolumeShaper$State;->(FF)V Landroid/media/VolumeShaper$State;->mVolume:F Landroid/media/VolumeShaper$State;->mXOffset:F -Landroid/metrics/LogMaker;->getCategory()I -Landroid/metrics/LogMaker;->getCounterBucket()J -Landroid/metrics/LogMaker;->getCounterName()Ljava/lang/String; -Landroid/metrics/LogMaker;->getCounterValue()I -Landroid/metrics/LogMaker;->getTimestamp()J -Landroid/metrics/LogMaker;->isLongCounterBucket()Z -Landroid/metrics/LogMaker;->serialize()[Ljava/lang/Object; -Landroid/metrics/MetricsReader;->checkpoint()V -Landroid/metrics/MetricsReader;->hasNext()Z -Landroid/metrics/MetricsReader;->()V -Landroid/metrics/MetricsReader;->next()Landroid/metrics/LogMaker; -Landroid/metrics/MetricsReader;->read(J)V -Landroid/metrics/MetricsReader;->reset()V Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String; Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String; Landroid/net/ConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties; @@ -1021,14 +883,9 @@ Landroid/net/ConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String; Landroid/net/ConnectivityManager;->getTetheredIfaces()[Ljava/lang/String; Landroid/net/ConnectivityManager;->isNetworkSupported(I)Z Landroid/net/ConnectivityManager;->isNetworkTypeMobile(I)Z -Landroid/net/ConnectivityManager;->isTetheringSupported()Z Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager; -Landroid/net/ConnectivityManager$OnStartTetheringCallback;->()V Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z -Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V -Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;)V -Landroid/net/ConnectivityManager;->stopTethering(I)V Landroid/net/ConnectivityManager;->TYPE_MOBILE_CBS:I Landroid/net/ConnectivityManager;->TYPE_MOBILE_EMERGENCY:I Landroid/net/ConnectivityManager;->TYPE_MOBILE_FOTA:I @@ -1043,15 +900,13 @@ Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableIfaces()[Ljava/lang/ Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String; Landroid/net/IConnectivityManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; +Landroid/net/IpConfiguration;->httpProxy:Landroid/net/ProxyInfo; Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V Landroid/net/LocalSocketImpl;->inboundFileDescriptors:[Ljava/io/FileDescriptor; Landroid/net/LocalSocketImpl;->outboundFileDescriptors:[Ljava/io/FileDescriptor; -Landroid/net/NetworkKey;->(Landroid/net/WifiKey;)V +Landroid/net/NetworkCapabilities;->getCapabilities()[I +Landroid/net/NetworkCapabilities;->getTransportTypes()[I Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager; -Landroid/net/NetworkRecommendationProvider;->(Landroid/content/Context;Ljava/util/concurrent/Executor;)V -Landroid/net/NetworkScoreManager;->clearScores()Z -Landroid/net/NetworkScoreManager;->getActiveScorerPackage()Ljava/lang/String; -Landroid/net/NetworkScoreManager;->updateScores([Landroid/net/ScoredNetwork;)Z Landroid/net/NetworkStats;->capacity:I Landroid/net/NetworkStats;->defaultNetwork:[I Landroid/net/NetworkStats;->iface:[Ljava/lang/String; @@ -1067,12 +922,6 @@ Landroid/net/NetworkStats;->txBytes:[J Landroid/net/NetworkStats;->txPackets:[J Landroid/net/NetworkStats;->uid:[I Landroid/net/ProxyInfo;->(Ljava/lang/String;ILjava/lang/String;)V -Landroid/net/RssiCurve;->(II[BI)V -Landroid/net/RssiCurve;->(II[B)V -Landroid/net/RssiCurve;->lookupScore(IZ)B -Landroid/net/ScoredNetwork;->(Landroid/net/NetworkKey;Landroid/net/RssiCurve;)V -Landroid/net/ScoredNetwork;->(Landroid/net/NetworkKey;Landroid/net/RssiCurve;ZLandroid/os/Bundle;)V -Landroid/net/ScoredNetwork;->(Landroid/net/NetworkKey;Landroid/net/RssiCurve;Z)V Landroid/net/SSLCertificateSocketFactory;->castToOpenSSLSocket(Ljava/net/Socket;)Lcom/android/org/conscrypt/OpenSSLSocketImpl; Landroid/net/SSLCertificateSocketFactory;->getAlpnSelectedProtocol(Ljava/net/Socket;)[B Landroid/net/SSLCertificateSocketFactory;->getDelegate()Ljavax/net/ssl/SSLSocketFactory; @@ -1095,28 +944,19 @@ Landroid/net/SSLCertificateSocketFactory;->setAlpnProtocols([[B)V Landroid/net/SSLCertificateSocketFactory;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V Landroid/net/SSLCertificateSocketFactory;->setSoWriteTimeout(Ljava/net/Socket;I)V Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String; -Landroid/net/SSLCertificateSocketFactory;->toLengthPrefixedList([[[B)[B Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V Landroid/net/SSLSessionCache;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache; Landroid/net/TrafficStats;->getRxBytes(Ljava/lang/String;)J Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService; Landroid/net/TrafficStats;->getTxBytes(Ljava/lang/String;)J -Landroid/net/TrafficStats;->setThreadStatsTagBackup()V -Landroid/net/TrafficStats;->setThreadStatsTagRestore()V -Landroid/net/TrafficStats;->setThreadStatsUid(I)V Landroid/net/Uri;->()V -Landroid/net/WebAddress;->(Ljava/lang/String;)V Landroid/net/wifi/IWifiManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiManager; Landroid/net/wifi/IWifiManager$Stub$Proxy;->(Landroid/os/IBinder;)V -Landroid/net/WifiKey;->(Ljava/lang/String;Ljava/lang/String;)V Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection; Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V -Landroid/net/wifi/RttManager;->getRttCapabilities()Landroid/net/wifi/RttManager$RttCapabilities; -Landroid/net/wifi/RttManager$RttParams;->()V -Landroid/net/wifi/RttManager;->startRanging([Landroid/net/wifi/RttManager$RttParams;Landroid/net/wifi/RttManager$RttListener;)V Landroid/net/wifi/ScanResult;->anqpDomainId:I Landroid/net/wifi/ScanResult;->anqpLines:Ljava/util/List; Landroid/net/wifi/ScanResult;->distanceCm:I @@ -1129,13 +969,7 @@ Landroid/net/wifi/ScanResult;->untrusted:Z Landroid/net/wifi/ScanResult;->wifiSsid:Landroid/net/wifi/WifiSsid; Landroid/net/wifi/WifiConfiguration;->apBand:I Landroid/net/wifi/WifiConfiguration;->apChannel:I -Landroid/net/wifi/WifiConfiguration;->creatorUid:I -Landroid/net/wifi/WifiConfiguration;->hasNoInternetAccess()Z -Landroid/net/wifi/WifiConfiguration;->isEphemeral()Z -Landroid/net/wifi/WifiConfiguration;->isNoInternetAccessExpected()Z -Landroid/net/wifi/WifiConfiguration;->lastUpdateUid:I Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration; -Landroid/net/wifi/WifiConfiguration;->numAssociation:I Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z Landroid/net/wifi/WifiEnterpriseConfig;->getCaCertificateAlias()Ljava/lang/String; Landroid/net/wifi/WifiEnterpriseConfig;->getClientCertificateAlias()Ljava/lang/String; @@ -1143,40 +977,13 @@ Landroid/net/wifi/WifiInfo;->getMeteredHint()Z Landroid/net/wifi/WifiInfo;->mMacAddress:Ljava/lang/String; Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V Landroid/net/wifi/WifiManager;->connect(ILandroid/net/wifi/WifiManager$ActionListener;)V -Landroid/net/wifi/WifiManager;->connect(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V -Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String; Landroid/net/wifi/WifiManager;->forget(ILandroid/net/wifi/WifiManager$ActionListener;)V -Landroid/net/wifi/WifiManager;->getPrivilegedConfiguredNetworks()Ljava/util/List; -Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration; -Landroid/net/wifi/WifiManager;->getWifiApState()I Landroid/net/wifi/WifiManager;->isDualBandSupported()Z -Landroid/net/wifi/WifiManager;->isWifiApEnabled()Z -Landroid/net/wifi/WifiManager;->isWifiScannerSupported()Z Landroid/net/wifi/WifiManager;->mService:Landroid/net/wifi/IWifiManager; Landroid/net/wifi/WifiManager;->save(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V -Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z -Landroid/net/wifi/WifiManager;->startScan(Landroid/os/WorkSource;)Z -Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String; -Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I -Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I -Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I -Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I -Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I -Landroid/net/wifi/WifiScanner;->getScanResults()Z -Landroid/net/wifi/WifiScanner$ScanData;->getResults()[Landroid/net/wifi/ScanResult; -Landroid/net/wifi/WifiScanner$ScanSettings;->()V -Landroid/net/wifi/WifiScanner;->startBackgroundScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;)V -Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;Landroid/os/WorkSource;)V -Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;)V -Landroid/net/wifi/WifiScanner;->stopBackgroundScan(Landroid/net/wifi/WifiScanner$ScanListener;)V Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String; -Landroid/nfc/NfcAdapter;->addNfcUnlockHandler(Landroid/nfc/NfcAdapter$NfcUnlockHandler;[Ljava/lang/String;)Z -Landroid/nfc/NfcAdapter;->disable()Z -Landroid/nfc/NfcAdapter;->enable()Z Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter; -Landroid/nfc/NfcAdapter;->removeNfcUnlockHandler(Landroid/nfc/NfcAdapter$NfcUnlockHandler;)Z Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V -Landroid/nfc/NfcAdapter;->setNdefPushMessage(Landroid/nfc/NdefMessage;Landroid/app/Activity;I)V Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext; Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper; Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread; @@ -1205,11 +1012,11 @@ Landroid/os/Binder;->execTransact(IJJI)Z Landroid/os/Binder;->mObject:J Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String; Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String; -Landroid/os/Build$VERSION;->RESOURCES_SDK_INT:I Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder; Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V Landroid/os/Debug;->countInstancesOfClass(Ljava/lang/Class;)J Landroid/os/Debug;->dumpReferenceTables()V +Landroid/os/Debug;->()V Landroid/os/Debug$MemoryInfo;->dalvikPrivateClean:I Landroid/os/Debug$MemoryInfo;->dalvikRss:I Landroid/os/Debug$MemoryInfo;->dalvikSharedClean:I @@ -1255,6 +1062,8 @@ Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback; Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger; Landroid/os/HwParcel;->(Z)V +Landroid/os/HwRemoteBinder;->()V +Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController; Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager; @@ -1288,7 +1097,6 @@ Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I Landroid/os/PowerManager;->isLightDeviceIdleMode()Z Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager; -Landroid/os/PowerManager;->userActivity(JII)V Landroid/os/PowerManager;->userActivity(JZ)V Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V @@ -1298,15 +1106,8 @@ Landroid/os/Process;->getPids(Ljava/lang/String;[I)[I Landroid/os/Process;->getTotalMemory()J Landroid/os/Process;->getUidForPid(I)I Landroid/os/Process;->isIsolated(I)Z -Landroid/os/Process;->isIsolated()Z Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V -Landroid/os/RecoverySystem;->cancelScheduledUpdate(Landroid/content/Context;)V -Landroid/os/RecoverySystem;->installPackage(Landroid/content/Context;Ljava/io/File;Z)V -Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;Landroid/os/Handler;)V -Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;)V -Landroid/os/RecoverySystem;->rebootWipeAb(Landroid/content/Context;Ljava/io/File;Ljava/lang/String;)V -Landroid/os/RecoverySystem;->scheduleUpdateOnBoot(Landroid/content/Context;Ljava/io/File;)V Landroid/os/SELinux;->isSELinuxEnabled()Z Landroid/os/SELinux;->isSELinuxEnforced()Z Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V @@ -1346,13 +1147,9 @@ Landroid/os/storage/VolumeInfo;->isVisible()Z Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V Landroid/os/StrictMode;->getThreadPolicyMask()I Landroid/os/StrictMode;->onBinderStrictModePolicyChange(I)V +Landroid/os/StrictMode$ThreadPolicy$Builder;->penaltyListener(Landroid/os/StrictMode$OnThreadViolationListener;Ljava/util/concurrent/Executor;)Landroid/os/StrictMode$ThreadPolicy$Builder; Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal; Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V -Landroid/os/SystemProperties;->getBoolean(Ljava/lang/String;Z)Z -Landroid/os/SystemProperties;->getInt(Ljava/lang/String;I)I -Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String; -Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; -Landroid/os/SystemProperties;->getLong(Ljava/lang/String;J)J Landroid/os/SystemProperties;->()V Landroid/os/SystemProperties;->native_get(Ljava/lang/String;)Ljava/lang/String; Landroid/os/SystemProperties;->PROP_NAME_MAX:I @@ -1366,15 +1163,6 @@ Landroid/os/Trace;->traceCounter(JLjava/lang/String;I)V Landroid/os/Trace;->traceEnd(J)V Landroid/os/Trace;->TRACE_TAG_APP:J Landroid/os/Trace;->TRACE_TAG_VIEW:J -Landroid/os/UpdateEngine;->applyPayload(Ljava/lang/String;JJ[Ljava/lang/String;)V -Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;Landroid/os/Handler;)Z -Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;)Z -Landroid/os/UpdateEngineCallback;->()V -Landroid/os/UpdateEngineCallback;->onPayloadApplicationComplete(I)V -Landroid/os/UpdateEngineCallback;->onStatusUpdate(IF)V -Landroid/os/UpdateEngine;->cancel()V -Landroid/os/UpdateEngine;->()V -Landroid/os/UpdateEngine;->resetStatus()V Landroid/os/UpdateLock;->acquire()V Landroid/os/UpdateLock;->isHeld()Z Landroid/os/UpdateLock;->NOW_IS_CONVENIENT:Ljava/lang/String; @@ -1384,13 +1172,9 @@ Landroid/os/UpdateLock;->UPDATE_LOCK_CHANGED:Ljava/lang/String; Landroid/os/UserHandle;->ALL:Landroid/os/UserHandle; Landroid/os/UserHandle;->getAppIdFromSharedAppGid(I)I Landroid/os/UserHandle;->getCallingUserId()I -Landroid/os/UserHandle;->getIdentifier()I Landroid/os/UserHandle;->getUid(II)I Landroid/os/UserHandle;->getUserId(I)I Landroid/os/UserHandle;->(I)V -Landroid/os/UserHandle;->isOwner()Z -Landroid/os/UserHandle;->myUserId()I -Landroid/os/UserHandle;->of(I)Landroid/os/UserHandle; Landroid/os/UserHandle;->PER_USER_RANGE:I Landroid/os/UserHandle;->USER_OWNER:I Landroid/os/UserManager;->getBadgedLabelForUser(Ljava/lang/CharSequence;Landroid/os/UserHandle;)Ljava/lang/CharSequence; @@ -1400,12 +1184,10 @@ Landroid/os/UserManager;->getProfiles(I)Ljava/util/List; Landroid/os/UserManager;->getUserHandle()I Landroid/os/UserManager;->getUserHandle(I)I Landroid/os/UserManager;->getUserInfo(I)Landroid/content/pm/UserInfo; -Landroid/os/UserManager;->getUserRestrictionSource(Ljava/lang/String;Landroid/os/UserHandle;)I Landroid/os/UserManager;->getUserSerialNumber(I)I Landroid/os/UserManager;->getUsers()Ljava/util/List; Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z Landroid/os/UserManager;->isLinkedUser()Z -Landroid/os/UserManager;->isManagedProfile()Z Landroid/os/UserManager;->isUserUnlocked(I)Z Landroid/os/VintfObject;->report()[Ljava/lang/String; Landroid/os/WorkSource;->add(ILjava/lang/String;)Z @@ -1435,6 +1217,7 @@ Landroid/preference/PreferenceManager;->getPreferenceScreen()Landroid/preference Landroid/preference/PreferenceManager;->inflateFromIntent(Landroid/content/Intent;Landroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen; Landroid/preference/PreferenceManager;->inflateFromResource(Landroid/content/Context;ILandroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen; Landroid/preference/PreferenceManager;->(Landroid/app/Activity;I)V +Landroid/preference/PreferenceManager;->(Landroid/content/Context;)V Landroid/preference/PreferenceManager;->mActivityDestroyListeners:Ljava/util/List; Landroid/preference/PreferenceManager;->mOnPreferenceTreeClickListener:Landroid/preference/PreferenceManager$OnPreferenceTreeClickListener; Landroid/preference/PreferenceManager;->mSharedPreferences:Landroid/content/SharedPreferences; @@ -1460,19 +1243,17 @@ Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;) Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V Landroid/provider/Settings$ContentProviderHolder;->mContentProvider:Landroid/content/IContentProvider; Landroid/provider/Settings$Global;->ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED:Ljava/lang/String; -Landroid/provider/Settings$Global;->OTA_DISABLE_AUTOMATIC_UPDATE:Ljava/lang/String; Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String; Landroid/provider/Settings$Global;->sNameValueCache:Landroid/provider/Settings$NameValueCache; Landroid/provider/Settings$NameValueCache;->mProviderHolder:Landroid/provider/Settings$ContentProviderHolder; Landroid/provider/Settings$Secure;->ACCESSIBILITY_AUTOCLICK_ENABLED:Ljava/lang/String; Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:Ljava/lang/String; -Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:Ljava/lang/String; Landroid/provider/Settings$Secure;->ACCESSIBILITY_LARGE_POINTER_ICON:Ljava/lang/String; +Landroid/provider/Settings$Secure;->ENABLED_NOTIFICATION_LISTENERS:Ljava/lang/String; Landroid/provider/Settings$Secure;->INCALL_POWER_BUTTON_BEHAVIOR:Ljava/lang/String; Landroid/provider/Settings$Secure;->LONG_PRESS_TIMEOUT:Ljava/lang/String; Landroid/provider/Settings$Secure;->PACKAGE_VERIFIER_USER_CONSENT:Ljava/lang/String; Landroid/provider/Settings$Secure;->sNameValueCache:Landroid/provider/Settings$NameValueCache; -Landroid/provider/Settings$Secure;->USER_SETUP_COMPLETE:Ljava/lang/String; Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String; Landroid/provider/Settings$System;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String; Landroid/provider/Settings$System;->HEARING_AID:Ljava/lang/String; @@ -1491,10 +1272,233 @@ Landroid/renderscript/RenderScriptCacheDir;->mCacheDir:Ljava/io/File; Landroid/renderscript/RenderScript;->create(Landroid/content/Context;I)Landroid/renderscript/RenderScript; Landroid/renderscript/RenderScript;->create(Landroid/content/Context;ILandroid/renderscript/RenderScript$ContextType;I)Landroid/renderscript/RenderScript; Landroid/renderscript/RenderScript;->getMinorID()J +Landroid/R$styleable;->ActionBar_background:I +Landroid/R$styleable;->ActionBar_backgroundSplit:I +Landroid/R$styleable;->ActionBar_backgroundStacked:I +Landroid/R$styleable;->ActionBar_divider:I +Landroid/R$styleable;->ActionBar:[I +Landroid/R$styleable;->ActionBar_itemPadding:I +Landroid/R$styleable;->CalendarView_dateTextAppearance:I +Landroid/R$styleable;->CalendarView_firstDayOfWeek:I +Landroid/R$styleable;->CalendarView_focusedMonthDateColor:I +Landroid/R$styleable;->CalendarView:[I +Landroid/R$styleable;->CalendarView_selectedDateVerticalBar:I +Landroid/R$styleable;->CalendarView_selectedWeekBackgroundColor:I +Landroid/R$styleable;->CalendarView_shownWeekCount:I +Landroid/R$styleable;->CalendarView_showWeekNumber:I +Landroid/R$styleable;->CalendarView_unfocusedMonthDateColor:I +Landroid/R$styleable;->CalendarView_weekDayTextAppearance:I +Landroid/R$styleable;->CalendarView_weekNumberColor:I +Landroid/R$styleable;->CalendarView_weekSeparatorLineColor:I +Landroid/R$styleable;->CheckedTextView_checkMark:I +Landroid/R$styleable;->CheckedTextView:[I +Landroid/R$styleable;->CompoundButton_button:I +Landroid/R$styleable;->CompoundButton:[I +Landroid/R$styleable;->ImageView_adjustViewBounds:I +Landroid/R$styleable;->ImageView_baselineAlignBottom:I +Landroid/R$styleable;->ImageView_cropToPadding:I +Landroid/R$styleable;->ImageView:[I +Landroid/R$styleable;->ImageView_maxHeight:I +Landroid/R$styleable;->ImageView_maxWidth:I +Landroid/R$styleable;->ImageView_scaleType:I +Landroid/R$styleable;->ImageView_src:I +Landroid/R$styleable;->ImageView_tint:I +Landroid/R$styleable;->LinearLayout_divider:I +Landroid/R$styleable;->LinearLayout_dividerPadding:I +Landroid/R$styleable;->LinearLayout:[I +Landroid/R$styleable;->LinearLayout_showDividers:I +Landroid/R$styleable;->ListView_dividerHeight:I +Landroid/R$styleable;->ListView_divider:I +Landroid/R$styleable;->ListView:[I +Landroid/R$styleable;->ProgressBar:[I +Landroid/R$styleable;->ProgressBar_indeterminateDrawable:I +Landroid/R$styleable;->ProgressBar_indeterminateDuration:I +Landroid/R$styleable;->ProgressBar_maxHeight:I +Landroid/R$styleable;->ProgressBar_maxWidth:I +Landroid/R$styleable;->ProgressBar_minHeight:I +Landroid/R$styleable;->ProgressBar_minWidth:I +Landroid/R$styleable;->ProgressBar_progressDrawable:I +Landroid/R$styleable;->SeekBar:[I +Landroid/R$styleable;->SeekBar_thumb:I +Landroid/R$styleable;->SeekBar_thumbOffset:I +Landroid/R$styleable;->Switch:[I +Landroid/R$styleable;->Switch_showText:I +Landroid/R$styleable;->Switch_splitTrack:I +Landroid/R$styleable;->Switch_switchMinWidth:I +Landroid/R$styleable;->Switch_switchPadding:I +Landroid/R$styleable;->Switch_switchTextAppearance:I +Landroid/R$styleable;->Switch_textOff:I +Landroid/R$styleable;->Switch_textOn:I +Landroid/R$styleable;->Switch_thumb:I +Landroid/R$styleable;->Switch_thumbTextPadding:I +Landroid/R$styleable;->Switch_track:I Landroid/R$styleable;->TextAppearance:[I +Landroid/R$styleable;->TextAppearance_textAllCaps:I +Landroid/R$styleable;->TextAppearance_textColorHighlight:I +Landroid/R$styleable;->TextAppearance_textColorHint:I Landroid/R$styleable;->TextAppearance_textColor:I +Landroid/R$styleable;->TextAppearance_textColorLink:I Landroid/R$styleable;->TextAppearance_textSize:I +Landroid/R$styleable;->TextAppearance_textStyle:I +Landroid/R$styleable;->TextAppearance_typeface:I +Landroid/R$styleable;->TextView_autoLink:I +Landroid/R$styleable;->TextView_autoText:I +Landroid/R$styleable;->TextView_bufferType:I +Landroid/R$styleable;->TextView_capitalize:I +Landroid/R$styleable;->TextView_cursorVisible:I +Landroid/R$styleable;->TextView_digits:I +Landroid/R$styleable;->TextView_drawableBottom:I +Landroid/R$styleable;->TextView_drawableEnd:I +Landroid/R$styleable;->TextView_drawableLeft:I +Landroid/R$styleable;->TextView_drawablePadding:I +Landroid/R$styleable;->TextView_drawableRight:I +Landroid/R$styleable;->TextView_drawableStart:I +Landroid/R$styleable;->TextView_drawableTop:I +Landroid/R$styleable;->TextView_editable:I +Landroid/R$styleable;->TextView_ellipsize:I +Landroid/R$styleable;->TextView_ems:I +Landroid/R$styleable;->TextView_enabled:I +Landroid/R$styleable;->TextView_freezesText:I +Landroid/R$styleable;->TextView_gravity:I +Landroid/R$styleable;->TextView_height:I +Landroid/R$styleable;->TextView_hint:I +Landroid/R$styleable;->TextView:[I +Landroid/R$styleable;->TextView_imeActionId:I +Landroid/R$styleable;->TextView_imeActionLabel:I +Landroid/R$styleable;->TextView_imeOptions:I +Landroid/R$styleable;->TextView_includeFontPadding:I +Landroid/R$styleable;->TextView_inputMethod:I +Landroid/R$styleable;->TextView_inputType:I +Landroid/R$styleable;->TextView_lines:I +Landroid/R$styleable;->TextView_lineSpacingExtra:I +Landroid/R$styleable;->TextView_lineSpacingMultiplier:I +Landroid/R$styleable;->TextView_linksClickable:I +Landroid/R$styleable;->TextView_marqueeRepeatLimit:I +Landroid/R$styleable;->TextView_maxEms:I +Landroid/R$styleable;->TextView_maxHeight:I +Landroid/R$styleable;->TextView_maxLength:I +Landroid/R$styleable;->TextView_maxLines:I +Landroid/R$styleable;->TextView_maxWidth:I +Landroid/R$styleable;->TextView_minEms:I +Landroid/R$styleable;->TextView_minHeight:I +Landroid/R$styleable;->TextView_minLines:I +Landroid/R$styleable;->TextView_minWidth:I +Landroid/R$styleable;->TextView_numeric:I +Landroid/R$styleable;->TextView_password:I +Landroid/R$styleable;->TextView_phoneNumber:I +Landroid/R$styleable;->TextView_privateImeOptions:I +Landroid/R$styleable;->TextView_scrollHorizontally:I +Landroid/R$styleable;->TextView_selectAllOnFocus:I +Landroid/R$styleable;->TextView_shadowColor:I +Landroid/R$styleable;->TextView_shadowDx:I +Landroid/R$styleable;->TextView_shadowDy:I +Landroid/R$styleable;->TextView_shadowRadius:I +Landroid/R$styleable;->TextView_singleLine:I +Landroid/R$styleable;->TextView_textAllCaps:I +Landroid/R$styleable;->TextView_textAppearance:I +Landroid/R$styleable;->TextView_textColorHighlight:I +Landroid/R$styleable;->TextView_textColorHint:I +Landroid/R$styleable;->TextView_textColor:I +Landroid/R$styleable;->TextView_textColorLink:I +Landroid/R$styleable;->TextView_textCursorDrawable:I +Landroid/R$styleable;->TextView_text:I +Landroid/R$styleable;->TextView_textIsSelectable:I +Landroid/R$styleable;->TextView_textScaleX:I +Landroid/R$styleable;->TextView_textSelectHandle:I +Landroid/R$styleable;->TextView_textSelectHandleLeft:I +Landroid/R$styleable;->TextView_textSelectHandleRight:I +Landroid/R$styleable;->TextView_textSize:I +Landroid/R$styleable;->TextView_textStyle:I +Landroid/R$styleable;->TextView_typeface:I +Landroid/R$styleable;->TextView_width:I +Landroid/R$styleable;->View_background:I +Landroid/R$styleable;->View_clickable:I +Landroid/R$styleable;->View_contentDescription:I +Landroid/R$styleable;->ViewDrawableStates:[I +Landroid/R$styleable;->View_drawingCacheQuality:I +Landroid/R$styleable;->View_duplicateParentState:I +Landroid/R$styleable;->View_fadingEdge:I +Landroid/R$styleable;->View_filterTouchesWhenObscured:I +Landroid/R$styleable;->View_fitsSystemWindows:I +Landroid/R$styleable;->View_focusable:I +Landroid/R$styleable;->View_focusableInTouchMode:I +Landroid/R$styleable;->View_hapticFeedbackEnabled:I +Landroid/R$styleable;->View:[I +Landroid/R$styleable;->View_id:I +Landroid/R$styleable;->View_isScrollContainer:I +Landroid/R$styleable;->View_keepScreenOn:I +Landroid/R$styleable;->View_longClickable:I +Landroid/R$styleable;->View_minHeight:I +Landroid/R$styleable;->View_minWidth:I +Landroid/R$styleable;->View_nextFocusDown:I +Landroid/R$styleable;->View_nextFocusLeft:I +Landroid/R$styleable;->View_nextFocusRight:I +Landroid/R$styleable;->View_nextFocusUp:I +Landroid/R$styleable;->View_onClick:I +Landroid/R$styleable;->View_overScrollMode:I +Landroid/R$styleable;->View_paddingBottom:I +Landroid/R$styleable;->View_paddingEnd:I +Landroid/R$styleable;->View_padding:I +Landroid/R$styleable;->View_paddingLeft:I +Landroid/R$styleable;->View_paddingRight:I +Landroid/R$styleable;->View_paddingStart:I +Landroid/R$styleable;->View_paddingTop:I +Landroid/R$styleable;->View_saveEnabled:I +Landroid/R$styleable;->View_scrollbarDefaultDelayBeforeFade:I +Landroid/R$styleable;->View_scrollbarFadeDuration:I +Landroid/R$styleable;->View_scrollbars:I +Landroid/R$styleable;->View_scrollbarSize:I +Landroid/R$styleable;->View_scrollbarStyle:I +Landroid/R$styleable;->View_scrollbarThumbHorizontal:I +Landroid/R$styleable;->View_scrollbarThumbVertical:I +Landroid/R$styleable;->View_scrollbarTrackHorizontal:I +Landroid/R$styleable;->View_scrollbarTrackVertical:I +Landroid/R$styleable;->View_scrollX:I +Landroid/R$styleable;->View_scrollY:I +Landroid/R$styleable;->View_soundEffectsEnabled:I +Landroid/R$styleable;->View_tag:I +Landroid/R$styleable;->View_visibility:I +Landroid/R$styleable;->Window:[I +Landroid/R$styleable;->Window_windowBackground:I +Landroid/R$styleable;->Window_windowFrame:I Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore; +Landroid/security/keystore/KeychainProtectionParams;->clearSecret()V +Landroid/security/keystore/KeychainProtectionParams;->getKeyDerivationParams()Landroid/security/keystore/KeyDerivationParams; +Landroid/security/keystore/KeychainProtectionParams;->getLockScreenUiFormat()I +Landroid/security/keystore/KeychainProtectionParams;->getSecret()[B +Landroid/security/keystore/KeychainProtectionParams;->getUserSecretType()I +Landroid/security/keystore/KeychainProtectionParams;->(IILandroid/security/keystore/KeyDerivationParams;[B)V +Landroid/security/keystore/KeychainProtectionParams;->(Landroid/os/Parcel;)V +Landroid/security/keystore/KeychainProtectionParams;->()V +Landroid/security/keystore/KeychainSnapshot;->getCounterId()J +Landroid/security/keystore/KeychainSnapshot;->getEncryptedRecoveryKeyBlob()[B +Landroid/security/keystore/KeychainSnapshot;->getKeychainProtectionParams()Ljava/util/List; +Landroid/security/keystore/KeychainSnapshot;->getMaxAttempts()I +Landroid/security/keystore/KeychainSnapshot;->getServerParams()[B +Landroid/security/keystore/KeychainSnapshot;->getSnapshotVersion()I +Landroid/security/keystore/KeychainSnapshot;->getTrustedHardwarePublicKey()[B +Landroid/security/keystore/KeychainSnapshot;->getWrappedApplicationKeys()Ljava/util/List; +Landroid/security/keystore/KeyDerivationParams;->ALGORITHM_ARGON2ID:I +Landroid/security/keystore/KeyDerivationParams;->ALGORITHM_SHA256:I +Landroid/security/keystore/KeyDerivationParams;->createSha256Params([B)Landroid/security/keystore/KeyDerivationParams; +Landroid/security/keystore/KeyDerivationParams;->getAlgorithm()I +Landroid/security/keystore/KeyDerivationParams;->getSalt()[B +Landroid/security/keystore/KeyDerivationParams;->(I[B)V +Landroid/security/keystore/KeyDerivationParams;->(Landroid/os/Parcel;)V +Landroid/security/keystore/RecoveryClaim;->getClaimBytes()[B +Landroid/security/keystore/RecoveryClaim;->getRecoverySession()Landroid/security/keystore/RecoverySession; +Landroid/security/keystore/RecoveryController;->getInstance()Landroid/security/keystore/RecoveryController; +Landroid/security/keystore/RecoveryController;->getRecoveryData([B)Landroid/security/keystore/KeychainSnapshot; +Landroid/security/keystore/RecoveryController;->initRecoveryService(Ljava/lang/String;[B)V +Landroid/security/keystore/RecoveryController;->recoverKeys(Landroid/security/keystore/RecoverySession;[BLjava/util/List;)Ljava/util/Map; +Landroid/security/keystore/RecoveryController;->setRecoverySecretTypes([I)V +Landroid/security/keystore/RecoveryController;->setRecoveryStatus(Ljava/lang/String;[Ljava/lang/String;I)V +Landroid/security/keystore/RecoveryController;->setServerParams([B)V +Landroid/security/keystore/RecoveryController;->setSnapshotCreatedPendingIntent(Landroid/app/PendingIntent;)V +Landroid/security/keystore/RecoveryController;->startRecoverySession([B[B[BLjava/util/List;)Landroid/security/keystore/RecoveryClaim; +Landroid/security/keystore/WrappedApplicationKey;->getAlias()Ljava/lang/String; +Landroid/security/keystore/WrappedApplicationKey;->getEncryptedKeyMaterial()[B +Landroid/security/keystore/WrappedApplicationKey;->(Ljava/lang/String;[B)V Landroid/security/net/config/RootTrustManager;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnectFailed()V Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnect(Ljava/lang/String;Landroid/media/session/MediaSession$Token;Landroid/os/Bundle;)V @@ -1505,38 +1509,11 @@ Landroid/service/media/MediaBrowserService;->KEY_MEDIA_ITEM:Ljava/lang/String; Landroid/service/media/MediaBrowserService$Result;->mFlags:I Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V -Landroid/service/persistentdata/PersistentDataBlockManager;->getFlashLockState()I -Landroid/service/persistentdata/PersistentDataBlockManager;->getMaximumDataBlockSize()J -Landroid/service/persistentdata/PersistentDataBlockManager;->read()[B -Landroid/service/persistentdata/PersistentDataBlockManager;->write([B)I -Landroid/service/resolver/ResolverRankerService;->()V -Landroid/service/resolver/ResolverTarget;->getChooserScore()F -Landroid/service/resolver/ResolverTarget;->getLaunchScore()F -Landroid/service/resolver/ResolverTarget;->getRecencyScore()F -Landroid/service/resolver/ResolverTarget;->getSelectProbability()F -Landroid/service/resolver/ResolverTarget;->getTimeSpentScore()F -Landroid/service/resolver/ResolverTarget;->setSelectProbability(F)V -Landroid/service/trust/TrustAgentService;->()V Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String; Landroid/system/Int32Ref;->value:I Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval; -Landroid/telecom/AudioState;->getRoute()I -Landroid/telecom/AudioState;->getSupportedRouteMask()I -Landroid/telecom/AudioState;->isMuted()Z -Landroid/telecom/Call;->addListener(Landroid/telecom/Call$Listener;)V -Landroid/telecom/Call$Listener;->()V -Landroid/telecom/Call;->removeListener(Landroid/telecom/Call$Listener;)V -Landroid/telecom/Phone;->addListener(Landroid/telecom/Phone$Listener;)V -Landroid/telecom/Phone;->getAudioState()Landroid/telecom/AudioState; -Landroid/telecom/Phone;->getCallAudioState()Landroid/telecom/CallAudioState; -Landroid/telecom/Phone;->getCalls()Ljava/util/List; -Landroid/telecom/Phone$Listener;->()V -Landroid/telecom/Phone;->removeListener(Landroid/telecom/Phone$Listener;)V -Landroid/telecom/Phone;->setAudioRoute(I)V -Landroid/telecom/Phone;->setMuted(Z)V -Landroid/telecom/TelecomManager;->endCall()Z Landroid/telecom/TelecomManager;->EXTRA_IS_HANDOVER:Ljava/lang/String; Landroid/telecom/TelecomManager;->getUserSelectedOutgoingPhoneAccount()Landroid/telecom/PhoneAccountHandle; Landroid/telecom/TelecomManager;->setUserSelectedOutgoingPhoneAccount(Landroid/telecom/PhoneAccountHandle;)V @@ -1544,6 +1521,9 @@ Landroid/telephony/CellSignalStrengthLte;->mCqi:I Landroid/telephony/CellSignalStrengthLte;->mRsrp:I Landroid/telephony/CellSignalStrengthLte;->mRsrq:I Landroid/telephony/CellSignalStrengthLte;->mRssnr:I +Landroid/telephony/CellSignalStrengthLte;->mSignalStrength:I +Landroid/telephony/CellSignalStrengthWcdma;->mBitErrorRate:I +Landroid/telephony/CellSignalStrengthWcdma;->mSignalStrength:I Landroid/telephony/PhoneStateListener;->mSubId:Ljava/lang/Integer; Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState; Landroid/telephony/SignalStrength;->getAsuLevel()I @@ -1566,24 +1546,18 @@ Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_GREAT:I Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_MODERATE:I Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_NONE_OR_UNKNOWN:I Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_POOR:I -Landroid/telephony/SmsManager;->RESULT_ERROR_FDN_CHECK_FAILURE:I Landroid/telephony/SmsMessage;->getSubId()I Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telephony/SmsMessageBase; Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoCount()I +Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoList()Ljava/util/List; Landroid/telephony/SubscriptionManager;->getDefaultDataSubscriptionInfo()Landroid/telephony/SubscriptionInfo; Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I Landroid/telephony/SubscriptionManager;->getPhoneId(I)I Landroid/telephony/SubscriptionManager;->getSlotIndex(I)I Landroid/telephony/SubscriptionManager;->getSubId(I)[I Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V -Landroid/telephony/TelephonyManager;->checkCarrierPrivilegesForPackage(Ljava/lang/String;)I Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager; Landroid/telephony/TelephonyManager;->getCallState(I)I -Landroid/telephony/TelephonyManager;->getCarrierPackageNamesForIntent(Landroid/content/Intent;)Ljava/util/List; -Landroid/telephony/TelephonyManager;->getCurrentPhoneType()I -Landroid/telephony/TelephonyManager;->getCurrentPhoneType(I)I -Landroid/telephony/TelephonyManager;->getDataEnabled(I)Z -Landroid/telephony/TelephonyManager;->getDataEnabled()Z Landroid/telephony/TelephonyManager;->getDataNetworkType(I)I Landroid/telephony/TelephonyManager;->getDefault()Landroid/telephony/TelephonyManager; Landroid/telephony/TelephonyManager;->getGroupIdLevel1(I)Ljava/lang/String; @@ -1603,6 +1577,7 @@ Landroid/telephony/TelephonyManager;->getSimOperator(I)Ljava/lang/String; Landroid/telephony/TelephonyManager;->getSimOperatorNameForPhone(I)Ljava/lang/String; Landroid/telephony/TelephonyManager;->getSimOperatorName(I)Ljava/lang/String; Landroid/telephony/TelephonyManager;->getSimOperatorNumericForPhone(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getSimOperatorNumeric(I)Ljava/lang/String; Landroid/telephony/TelephonyManager;->getSimSerialNumber(I)Ljava/lang/String; Landroid/telephony/TelephonyManager;->getSubIdForPhoneAccount(Landroid/telecom/PhoneAccount;)I Landroid/telephony/TelephonyManager;->getSubscriberId(I)Ljava/lang/String; @@ -1613,7 +1588,6 @@ Landroid/telephony/TelephonyManager;->()V Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z Landroid/telephony/TelephonyManager;->isNetworkRoaming(I)Z Landroid/telephony/TelephonyManager;->isVolteAvailable()Z -Landroid/telephony/TelephonyManager;->setDataEnabled(IZ)V Landroid/text/AndroidBidi;->bidi(I[C[B)I Landroid/text/DynamicLayout;->(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout; @@ -1657,6 +1631,7 @@ Landroid/text/SpannableStringInternal;->sendSpanRemoved(Ljava/lang/Object;II)V Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;III)V Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;IIIZ)V Landroid/text/SpannableStringInternal;->START:I +Landroid/text/SpanSet;->spans:[Ljava/lang/Object; Landroid/text/StaticLayout;->(Ljava/lang/CharSequence;IILandroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZLandroid/text/TextUtils$TruncateAt;II)V Landroid/text/StaticLayout$LineBreaks;->ascents:[F Landroid/text/StaticLayout$LineBreaks;->breaks:[I @@ -1666,39 +1641,56 @@ Landroid/text/StaticLayout$LineBreaks;->widths:[F Landroid/text/StaticLayout;->mColumns:I Landroid/text/StaticLayout;->mLineCount:I Landroid/text/StaticLayout;->mLines:[I +Landroid/text/TextLine;->mCharacterStyleSpanSet:Landroid/text/SpanSet; +Landroid/text/TextLine;->mMetricAffectingSpanSpanSet:Landroid/text/SpanSet; +Landroid/text/TextLine;->mReplacementSpanSpanSet:Landroid/text/SpanSet; +Landroid/text/TextLine;->mSpanned:Landroid/text/Spanned; +Landroid/text/TextLine;->mText:Ljava/lang/CharSequence; Landroid/text/TextLine;->obtain()Landroid/text/TextLine; Landroid/text/TextLine;->sCached:[Landroid/text/TextLine; Landroid/text/TextPaint;->setUnderlineText(IF)V Landroid/transition/ChangeBounds;->BOTTOM_RIGHT_ONLY_PROPERTY:Landroid/util/Property; Landroid/transition/ChangeBounds;->POSITION_PROPERTY:Landroid/util/Property; +Landroid/transition/TransitionManager;->sRunningTransitions:Ljava/lang/ThreadLocal; Landroid/util/ArrayMap;->mBaseCacheSize:I Landroid/util/ArrayMap;->mTwiceBaseCacheSize:I Landroid/util/DisplayMetrics;->noncompatHeightPixels:I Landroid/util/DisplayMetrics;->noncompatWidthPixels:I Landroid/util/EventLog$Event;->([B)V Landroid/util/Log;->wtf(ILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ZZ)I +Landroid/util/LongSparseLongArray;->mKeys:[J +Landroid/util/LongSparseLongArray;->mSize:I +Landroid/util/LongSparseLongArray;->mValues:[J +Landroid/util/NtpTrustedTime;->forceRefresh()Z +Landroid/util/NtpTrustedTime;->getCachedNtpTime()J +Landroid/util/NtpTrustedTime;->getCachedNtpTimeReference()J +Landroid/util/NtpTrustedTime;->getInstance(Landroid/content/Context;)Landroid/util/NtpTrustedTime; +Landroid/util/NtpTrustedTime;->hasCache()Z Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object; Landroid/util/Rational;->readObject(Ljava/io/ObjectInputStream;)V Landroid/util/Singleton;->mInstance:Ljava/lang/Object; +Landroid/util/SparseIntArray;->mKeys:[I +Landroid/util/SparseIntArray;->mSize:I +Landroid/util/SparseIntArray;->mValues:[I Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager; Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z +Landroid/view/accessibility/AccessibilityManager;->mIsEnabled:Z Landroid/view/accessibility/AccessibilityManager;->mIsHighTextContrastEnabled:Z Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager; Landroid/view/accessibility/AccessibilityManager;->sInstanceSync:Ljava/lang/Object; Landroid/view/accessibility/AccessibilityNodeInfo;->refresh(Landroid/os/Bundle;Z)Z +Landroid/view/accessibility/AccessibilityRecord;->getSourceNodeId()J Landroid/view/accessibility/IAccessibilityManager;->getEnabledAccessibilityServiceList(II)Ljava/util/List; Landroid/view/accessibility/IAccessibilityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/accessibility/IAccessibilityManager; Landroid/view/accessibility/IAccessibilityManager$Stub$Proxy;->(Landroid/os/IBinder;)V -Landroid/view/ActionMode;->isUiFocusable()Z Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener; +Landroid/view/autofill/IAutoFillManager$Stub$Proxy;->(Landroid/os/IBinder;)V Landroid/view/Choreographer$CallbackQueue;->addCallbackLocked(JLjava/lang/Object;Ljava/lang/Object;)V Landroid/view/Choreographer;->doFrame(JI)V Landroid/view/Choreographer;->getFrameTime()J Landroid/view/Choreographer;->mCallbackQueues:[Landroid/view/Choreographer$CallbackQueue; Landroid/view/Choreographer;->mLastFrameTimeNanos:J Landroid/view/Choreographer;->mLock:Ljava/lang/Object; -Landroid/view/Choreographer;->postCallback(ILjava/lang/Runnable;Ljava/lang/Object;)V -Landroid/view/Choreographer;->removeCallbacks(ILjava/lang/Runnable;Ljava/lang/Object;)V Landroid/view/Choreographer;->scheduleVsyncLocked()V Landroid/view/Choreographer;->USE_VSYNC:Z Landroid/view/ContextThemeWrapper;->getThemeResId()I @@ -1795,6 +1787,7 @@ Landroid/view/PointerIcon;->mHotSpotY:F Landroid/view/PointerIcon;->mType:I Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener; +Landroid/view/ScaleGestureDetector;->mMinSpan:I Landroid/view/SurfaceControl$PhysicalDisplayInfo;->appVsyncOffsetNanos:J Landroid/view/SurfaceControl$PhysicalDisplayInfo;->density:F Landroid/view/SurfaceControl$PhysicalDisplayInfo;->height:I @@ -1824,6 +1817,7 @@ Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;- Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z +Landroid/view/TextureView;->destroyHardwareLayer()V Landroid/view/TextureView;->mLayer:Landroid/view/TextureLayer; Landroid/view/TextureView;->mNativeWindow:J Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture; @@ -1902,8 +1896,10 @@ Landroid/view/View;->mViewFlags:I Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V Landroid/view/View;->recomputePadding()V Landroid/view/View;->requestAccessibilityFocus()Z +Landroid/view/View;->resetDisplayList()V Landroid/view/View;->resetPaddingToInitialValues()V Landroid/view/ViewRootImpl;->detachFunctor(J)V +Landroid/view/ViewRootImpl;->enqueueInputEvent(Landroid/view/InputEvent;)V Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V Landroid/view/ViewRootImpl;->mStopped:Z Landroid/view/ViewRootImpl;->mView:Landroid/view/View; @@ -1948,89 +1944,28 @@ Landroid/view/WindowManagerImpl;->mGlobal:Landroid/view/WindowManagerGlobal; Landroid/view/WindowManager$LayoutParams;->hideTimeoutMilliseconds:J Landroid/view/WindowManager$LayoutParams;->needsMenuKey:I Landroid/view/WindowManager$LayoutParams;->NEEDS_MENU_SET_TRUE:I -Landroid/view/WindowManager$LayoutParams;->PRIVATE_FLAG_NO_MOVE_ANIMATION:I -Landroid/view/WindowManager$LayoutParams;->privateFlags:I Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J Landroid/view/Window;->mAppName:Ljava/lang/String; Landroid/view/Window;->mAppToken:Landroid/os/IBinder; Landroid/view/Window;->mHardwareAccelerated:Z -Landroid/webkit/FindActionModeCallback;->findAll()V -Landroid/webkit/FindActionModeCallback;->(Landroid/content/Context;)V -Landroid/webkit/FindActionModeCallback;->setText(Ljava/lang/String;)V -Landroid/webkit/FindActionModeCallback;->setWebView(Landroid/webkit/WebView;)V -Landroid/webkit/FindActionModeCallback;->showSoftInput()V -Landroid/webkit/GeolocationPermissions;->()V -Landroid/webkit/HttpAuthHandler;->()V Landroid/webkit/IWebViewUpdateService$Stub$Proxy;->(Landroid/os/IBinder;)V -Landroid/webkit/JsDialogHelper;->(Landroid/webkit/JsPromptResult;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V -Landroid/webkit/JsDialogHelper;->showDialog(Landroid/content/Context;)V -Landroid/webkit/JsPromptResult;->getStringResult()Ljava/lang/String; -Landroid/webkit/JsPromptResult;->(Landroid/webkit/JsResult$ResultReceiver;)V -Landroid/webkit/SslErrorHandler;->()V -Landroid/webkit/TokenBindingService;->()V -Landroid/webkit/TokenBindingService$TokenBindingKey;->()V -Landroid/webkit/WebChromeClient;->openFileChooser(Landroid/webkit/ValueCallback;Ljava/lang/String;Ljava/lang/String;)V -Landroid/webkit/WebMessagePort;->()V -Landroid/webkit/WebResourceError;->()V -Landroid/webkit/WebResourceResponse;->(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/util/Map;Ljava/io/InputStream;)V Landroid/webkit/WebResourceResponse;->mImmutable:Z -Landroid/webkit/WebSettings;->getAcceptThirdPartyCookies()Z -Landroid/webkit/WebSettings;->setAcceptThirdPartyCookies(Z)V -Landroid/webkit/WebSettings;->setNavDump(Z)V -Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V -Landroid/webkit/WebStorage;->()V -Landroid/webkit/WebStorage$Origin;->(Ljava/lang/String;JJ)V Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler; Landroid/webkit/WebView;->debugDump()V -Landroid/webkit/WebViewDelegate;->addWebViewAssetPath(Landroid/content/Context;)V -Landroid/webkit/WebViewDelegate;->callDrawGlFunction(Landroid/graphics/Canvas;JLjava/lang/Runnable;)V -Landroid/webkit/WebViewDelegate;->callDrawGlFunction(Landroid/graphics/Canvas;J)V -Landroid/webkit/WebViewDelegate;->canInvokeDrawGlFunctor(Landroid/view/View;)Z -Landroid/webkit/WebViewDelegate;->detachDrawGlFunctor(Landroid/view/View;J)V -Landroid/webkit/WebViewDelegate;->getApplication()Landroid/app/Application; -Landroid/webkit/WebViewDelegate;->getDataDirectorySuffix()Ljava/lang/String; -Landroid/webkit/WebViewDelegate;->getErrorString(Landroid/content/Context;I)Ljava/lang/String; -Landroid/webkit/WebViewDelegate;->getPackageId(Landroid/content/res/Resources;Ljava/lang/String;)I -Landroid/webkit/WebViewDelegate;->invokeDrawGlFunctor(Landroid/view/View;JZ)V -Landroid/webkit/WebViewDelegate;->isMultiProcessEnabled()Z -Landroid/webkit/WebViewDelegate;->isTraceTagEnabled()Z -Landroid/webkit/WebViewDelegate;->setOnTraceEnabledChangeListener(Landroid/webkit/WebViewDelegate$OnTraceEnabledChangeListener;)V Landroid/webkit/WebView;->disablePlatformNotifications()V Landroid/webkit/WebView;->emulateShiftHeld()V Landroid/webkit/WebView;->enablePlatformNotifications()V -Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo; Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider; Landroid/webkit/WebViewFactory;->getWebViewContextAndSetProvider()Landroid/content/Context; -Landroid/webkit/WebViewFactory;->loadWebViewNativeLibraryFromPackage(Ljava/lang/String;Ljava/lang/ClassLoader;)I -Landroid/webkit/WebViewFactoryProvider;->getStatics()Landroid/webkit/WebViewFactoryProvider$Statics; -Landroid/webkit/WebViewFactoryProvider$Statics;->getDefaultUserAgent(Landroid/content/Context;)Ljava/lang/String; Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo; Landroid/webkit/WebViewFactory;->sProviderInstance:Landroid/webkit/WebViewFactoryProvider; Landroid/webkit/WebView;->getVisibleTitleHeight()I -Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider; -Landroid/webkit/WebView$HitTestResult;->()V -Landroid/webkit/WebView$HitTestResult;->setExtra(Ljava/lang/String;)V -Landroid/webkit/WebView$HitTestResult;->setType(I)V Landroid/webkit/WebView;->isPaused()Z Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider; Landroid/webkit/WebView;->notifyFindDialogDismissed()V -Landroid/webkit/WebView$PrivateAccess;->overScrollBy(IIIIIIIIZ)V -Landroid/webkit/WebView$PrivateAccess;->setMeasuredDimension(II)V -Landroid/webkit/WebView$PrivateAccess;->super_dispatchKeyEvent(Landroid/view/KeyEvent;)Z -Landroid/webkit/WebView$PrivateAccess;->super_getScrollBarStyle()I -Landroid/webkit/WebView$PrivateAccess;->super_onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V -Landroid/webkit/WebView$PrivateAccess;->super_onGenericMotionEvent(Landroid/view/MotionEvent;)Z -Landroid/webkit/WebView$PrivateAccess;->super_performAccessibilityAction(ILandroid/os/Bundle;)Z -Landroid/webkit/WebView$PrivateAccess;->super_performLongClick()Z -Landroid/webkit/WebView$PrivateAccess;->super_requestFocus(ILandroid/graphics/Rect;)Z -Landroid/webkit/WebView$PrivateAccess;->super_scrollTo(II)V -Landroid/webkit/WebView$PrivateAccess;->super_setFrame(IIII)Z -Landroid/webkit/WebView$PrivateAccess;->super_setLayoutParams(Landroid/view/ViewGroup$LayoutParams;)V -Landroid/webkit/WebView$PrivateAccess;->super_startActivityForResult(Landroid/content/Intent;I)V Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z Landroid/webkit/WebView;->sEnforceThreadChecking:Z -Landroid/webkit/WebViewUpdateService;->getCurrentWebViewPackageName()Ljava/lang/String; Landroid/widget/AbsListView$FlingRunnable;->endFling()V Landroid/widget/AbsListView$FlingRunnable;->mScroller:Landroid/widget/OverScroller; Landroid/widget/AbsListView$FlingRunnable;->start(I)V @@ -2063,6 +1998,7 @@ Landroid/widget/AbsSeekBar;->mIsDragging:Z Landroid/widget/AbsSeekBar;->mSplitTrack:Z Landroid/widget/AbsSeekBar;->mThumb:Landroid/graphics/drawable/Drawable; Landroid/widget/AbsSeekBar;->mTouchProgressOffset:F +Landroid/widget/ActivityChooserModel;->get(Landroid/content/Context;Ljava/lang/String;)Landroid/widget/ActivityChooserModel; Landroid/widget/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V Landroid/widget/AdapterView;->mDataChanged:Z Landroid/widget/AdapterView;->mFirstPosition:I @@ -2076,10 +2012,17 @@ Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable; Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate; Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint; +Landroid/widget/Editor;->invalidateTextDisplayList()V Landroid/widget/Editor;->mShowCursor:J Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable; +Landroid/widget/FastScroller;->mContainerRect:Landroid/graphics/Rect; +Landroid/widget/FastScroller;->mHeaderCount:I +Landroid/widget/FastScroller;->mLongList:Z +Landroid/widget/FastScroller;->mMinimumTouchTarget:I Landroid/widget/FastScroller;->mThumbDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/FastScroller;->mThumbImage:Landroid/widget/ImageView; Landroid/widget/FastScroller;->mTrackDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/FastScroller;->mTrackImage:Landroid/widget/ImageView; Landroid/widget/Gallery;->fillToGalleryLeft()V Landroid/widget/Gallery;->fillToGalleryRight()V Landroid/widget/Gallery$FlingRunnable;->startUsingVelocity(I)V @@ -2164,6 +2107,8 @@ Landroid/widget/RelativeLayout$LayoutParams;->mTop:I Landroid/widget/RelativeLayout;->mGravity:I Landroid/widget/RemoteViews$Action;->mergeBehavior()I Landroid/widget/RemoteViews$Action;->viewId:I +Landroid/widget/RemoteViewsAdapter;->mCache:Landroid/widget/RemoteViewsAdapter$FixedSizeRemoteViewsCache; +Landroid/widget/RemoteViewsAdapter;->mWorkerThread:Landroid/os/HandlerThread; Landroid/widget/RemoteViews$BitmapCache;->mBitmaps:Ljava/util/ArrayList; Landroid/widget/RemoteViews$BitmapReflectionAction;->bitmap:Landroid/graphics/Bitmap; Landroid/widget/RemoteViews$BitmapReflectionAction;->methodName:Ljava/lang/String; @@ -2187,6 +2132,7 @@ Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller; Landroid/widget/SearchView;->mCloseButton:Landroid/widget/ImageView; Landroid/widget/SearchView;->mSearchButton:Landroid/widget/ImageView; Landroid/widget/SearchView;->mSearchPlate:Landroid/view/View; +Landroid/widget/SearchView;->mSearchSrcTextView:Landroid/widget/SearchView$SearchAutoComplete; Landroid/widget/SearchView;->onCloseClicked()V Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V Landroid/widget/SlidingDrawer;->mTopOffset:I @@ -2197,6 +2143,7 @@ Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$Conte Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V Landroid/widget/TextView;->assumeLayout()V Landroid/widget/TextView;->createEditorIfNeeded()V +Landroid/widget/TextView;->isSingleLine()Z Landroid/widget/TextView;->mCursorDrawableRes:I Landroid/widget/TextView;->mCurTextColor:I Landroid/widget/TextView;->mEditor:Landroid/widget/Editor; @@ -2220,8 +2167,10 @@ Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lan Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->(Landroid/os/IBinder;)V Lcom/android/internal/app/IBatteryStats;->getStatistics()[B Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats; +Lcom/android/internal/app/IBatteryStats$Stub$Proxy;->(Landroid/os/IBinder;)V Lcom/android/internal/app/IVoiceInteractionManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IVoiceInteractionManagerService; Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z +Lcom/android/internal/location/ILocationProvider$Stub;->()V Lcom/android/internal/os/BatterySipper;->add(Lcom/android/internal/os/BatterySipper;)V Lcom/android/internal/os/BatterySipper;->drainType:Lcom/android/internal/os/BatterySipper$DrainType; Lcom/android/internal/os/BatterySipper;->getUid()I @@ -2271,6 +2220,7 @@ Lcom/android/internal/R$array;->config_mobile_hotspot_provision_app:I Lcom/android/internal/R$array;->config_tether_wifi_regexs:I Lcom/android/internal/R$array;->maps_starting_lat_lng:I Lcom/android/internal/R$array;->maps_starting_zoom:I +Lcom/android/internal/R$attr;->actionBarStyle:I Lcom/android/internal/R$attr;->mapViewStyle:I Lcom/android/internal/R$attr;->state_focused:I Lcom/android/internal/R$attr;->state_pressed:I @@ -2306,6 +2256,12 @@ Lcom/android/internal/R$id;->title:I Lcom/android/internal/R$integer;->config_screenBrightnessDim:I Lcom/android/internal/R$integer;->config_toastDefaultGravity:I Lcom/android/internal/R$layout;->screen_title:I +Lcom/android/internal/R$string;->byteShort:I +Lcom/android/internal/R$string;->gigabyteShort:I +Lcom/android/internal/R$string;->kilobyteShort:I +Lcom/android/internal/R$string;->megabyteShort:I +Lcom/android/internal/R$string;->petabyteShort:I +Lcom/android/internal/R$string;->terabyteShort:I Lcom/android/internal/R$styleable;->AccountAuthenticator_accountPreferences:I Lcom/android/internal/R$styleable;->AccountAuthenticator_accountType:I Lcom/android/internal/R$styleable;->AccountAuthenticator_customTokens:I @@ -2334,6 +2290,10 @@ Lcom/android/internal/R$styleable;->AndroidManifestUsesSdk_minSdkVersion:I Lcom/android/internal/R$styleable;->AndroidManifestUsesSdk_targetSdkVersion:I Lcom/android/internal/R$styleable;->AndroidManifest_versionCode:I Lcom/android/internal/R$styleable;->AndroidManifest_versionName:I +Lcom/android/internal/R$styleable;->CheckBoxPreference_disableDependentsState:I +Lcom/android/internal/R$styleable;->CheckBoxPreference:[I +Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOff:I +Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOn:I Lcom/android/internal/R$styleable;->CompoundButton_button:I Lcom/android/internal/R$styleable;->CompoundButton:[I Lcom/android/internal/R$styleable;->EdgeEffect_colorEdgeEffect:I @@ -2341,6 +2301,23 @@ Lcom/android/internal/R$styleable;->EdgeEffect:[I Lcom/android/internal/R$styleable;->IconMenuView:[I Lcom/android/internal/R$styleable;->ImageView:[I Lcom/android/internal/R$styleable;->ImageView_src:I +Lcom/android/internal/R$styleable;->Preference_defaultValue:I +Lcom/android/internal/R$styleable;->Preference_dependency:I +Lcom/android/internal/R$styleable;->Preference_enabled:I +Lcom/android/internal/R$styleable;->Preference_fragment:I +Lcom/android/internal/R$styleable;->PreferenceGroup:[I +Lcom/android/internal/R$styleable;->PreferenceGroup_orderingFromXml:I +Lcom/android/internal/R$styleable;->Preference:[I +Lcom/android/internal/R$styleable;->Preference_icon:I +Lcom/android/internal/R$styleable;->Preference_key:I +Lcom/android/internal/R$styleable;->Preference_layout:I +Lcom/android/internal/R$styleable;->Preference_order:I +Lcom/android/internal/R$styleable;->Preference_persistent:I +Lcom/android/internal/R$styleable;->Preference_selectable:I +Lcom/android/internal/R$styleable;->Preference_shouldDisableView:I +Lcom/android/internal/R$styleable;->Preference_summary:I +Lcom/android/internal/R$styleable;->Preference_title:I +Lcom/android/internal/R$styleable;->Preference_widgetLayout:I Lcom/android/internal/R$styleable;->ScrollView_fillViewport:I Lcom/android/internal/R$styleable;->ScrollView:[I Lcom/android/internal/R$styleable;->SyncAdapter_accountType:I @@ -2357,6 +2334,7 @@ Lcom/android/internal/R$styleable;->TextView_drawableLeft:I Lcom/android/internal/R$styleable;->TextView_drawableRight:I Lcom/android/internal/R$styleable;->TextView_drawableTop:I Lcom/android/internal/R$styleable;->TextView:[I +Lcom/android/internal/R$styleable;->TextView_maxLines:I Lcom/android/internal/R$styleable;->View_background:I Lcom/android/internal/R$styleable;->ViewGroup_Layout:[I Lcom/android/internal/R$styleable;->ViewGroup_Layout_layout_height:I @@ -2378,12 +2356,16 @@ Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->(Landroid/os/IB Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms; Lcom/android/internal/telephony/ISub$Stub$Proxy;->(Landroid/os/IBinder;)V Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V +Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z +Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z Lcom/android/internal/telephony/ITelephony;->endCall()Z Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->silenceRinger()V Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony; +Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->(Landroid/os/IBinder;)V Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I +Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;->(Landroid/os/IBinder;)V Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap; Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager; Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;->getEnabledInputMethodList()Ljava/util/List; @@ -2394,6 +2376,7 @@ Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/C Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V Lcom/android/internal/view/menu/MenuItemImpl;->mIconResId:I Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V +Lcom/android/internal/view/menu/MenuPopupHelper;->setForceShowIcon(Z)V Lcom/android/internal/view/menu/MenuView$ItemView;->getItemData()Lcom/android/internal/view/menu/MenuItemImpl; Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I @@ -2403,6 +2386,7 @@ Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/Connection Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List; Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns; Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient; +Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V Lcom/android/okhttp/okio/ByteString;->readObject(Ljava/io/ObjectInputStream;)V Lcom/android/okhttp/okio/ByteString;->writeObject(Ljava/io/ObjectOutputStream;)V Lcom/android/org/conscrypt/AbstractConscryptSocket;->getAlpnSelectedProtocol()[B @@ -2414,8 +2398,8 @@ Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostname()Ljava/lang/Str Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostnameOrIP()Ljava/lang/String; Lcom/android/org/conscrypt/AbstractConscryptSocket;->getNpnSelectedProtocol()[B Lcom/android/org/conscrypt/AbstractConscryptSocket;->getSoWriteTimeout()I -Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([Ljava/lang/String;)V Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([B)V +Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([Ljava/lang/String;)V Lcom/android/org/conscrypt/AbstractConscryptSocket;->setApplicationProtocols([Ljava/lang/String;)V Lcom/android/org/conscrypt/AbstractConscryptSocket;->setChannelIdEnabled(Z)V Lcom/android/org/conscrypt/AbstractConscryptSocket;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V @@ -2436,8 +2420,8 @@ Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String; Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostnameOrIP()Ljava/lang/String; Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getNpnSelectedProtocol()[B Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getSoWriteTimeout()I -Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([Ljava/lang/String;)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V +Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([Ljava/lang/String;)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setChannelIdEnabled(Z)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHandshakeTimeout(I)V @@ -2445,6 +2429,7 @@ Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHostname(Ljava/lang/String;)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setNpnProtocols([B)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setSoWriteTimeout(I)V Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setUseSessionTickets(Z)V +Lcom/android/org/conscrypt/OpenSSLX509Certificate;->mContext:J Lcom/android/org/conscrypt/TrustManagerImpl;->(Ljava/security/KeyStore;)V Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String; Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList; @@ -2465,6 +2450,7 @@ Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile; Ldalvik/system/DexPathList$Element;->(Ldalvik/system/DexFile;Ljava/io/File;)V Ldalvik/system/DexPathList$Element;->(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V Ldalvik/system/DexPathList;->(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V +Ldalvik/system/DexPathList;->loadDexFile(Ljava/io/File;Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile; Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element; Ldalvik/system/DexPathList;->makeInMemoryDexElements([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element; Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement; @@ -2553,6 +2539,7 @@ Ljava/lang/StringBuffer;->writeObject(Ljava/io/ObjectOutputStream;)V Ljava/lang/StringBuilder;->readObject(Ljava/io/ObjectInputStream;)V Ljava/lang/StringBuilder;->writeObject(Ljava/io/ObjectOutputStream;)V Ljava/lang/String;->(II[C)V +Ljava/lang/System;->()V Ljava/lang/Thread;->daemon:Z Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V Ljava/lang/ThreadGroup;->add(Ljava/lang/Thread;)V @@ -2613,6 +2600,7 @@ Ljava/net/Socket;->impl:Ljava/net/SocketImpl; Ljava/net/URI;->host:Ljava/lang/String; Ljava/net/URI;->readObject(Ljava/io/ObjectInputStream;)V Ljava/net/URI;->writeObject(Ljava/io/ObjectOutputStream;)V +Ljava/net/URL;->handler:Ljava/net/URLStreamHandler; Ljava/net/URL;->handlers:Ljava/util/Hashtable; Ljava/net/URL;->readObject(Ljava/io/ObjectInputStream;)V Ljava/net/URL;->writeObject(Ljava/io/ObjectOutputStream;)V @@ -2709,6 +2697,7 @@ Ljava/util/concurrent/atomic/DoubleAccumulator;->readObject(Ljava/io/ObjectInput Ljava/util/concurrent/atomic/DoubleAdder;->readObject(Ljava/io/ObjectInputStream;)V Ljava/util/concurrent/atomic/LongAccumulator;->readObject(Ljava/io/ObjectInputStream;)V Ljava/util/concurrent/atomic/LongAdder;->readObject(Ljava/io/ObjectInputStream;)V +Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z Ljava/util/concurrent/ConcurrentHashMap;->readObject(Ljava/io/ObjectInputStream;)V Ljava/util/concurrent/ConcurrentHashMap;->writeObject(Ljava/io/ObjectOutputStream;)V Ljava/util/concurrent/ConcurrentLinkedDeque;->readObject(Ljava/io/ObjectInputStream;)V @@ -2757,6 +2746,7 @@ Ljava/util/IdentityHashMap;->writeObject(Ljava/io/ObjectOutputStream;)V Ljava/util/InvalidPropertiesFormatException;->readObject(Ljava/io/ObjectInputStream;)V Ljava/util/InvalidPropertiesFormatException;->writeObject(Ljava/io/ObjectOutputStream;)V Ljava/util/LinkedHashMap;->eldest()Ljava/util/Map$Entry; +Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z Ljava/util/LinkedList;->readObject(Ljava/io/ObjectInputStream;)V Ljava/util/LinkedList;->writeObject(Ljava/io/ObjectOutputStream;)V Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3d04e2c64bee2cd492204de0aefdce77946f837b..99c5d2b9a92738cd8dae6e853203d7724c5b7c60 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6366,6 +6366,8 @@ public class Activity extends ContextThemeWrapper } else { writer.print(prefix); writer.println("No AutofillManager"); } + + ResourcesManager.getInstance().dump(prefix, writer); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 03faeeeb91a1fcebd712cc193d074b3aa9e2ef7f..2d73ce0c059452f7e6c0ce76bcbede38eeac8dad 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -44,7 +44,6 @@ import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; -import android.net.Uri; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -71,6 +70,7 @@ import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.RoSystemProperties; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.MemInfoReader; import com.android.server.LocalServices; import org.xmlpull.v1.XmlSerializer; @@ -963,6 +963,17 @@ public class ActivityManager { !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel); } + /** + * Return the total number of bytes of RAM this device has. + * @hide + */ + @TestApi + public long getTotalRam() { + MemInfoReader memreader = new MemInfoReader(); + memreader.readMemInfo(); + return memreader.getTotalSize(); + } + /** * Return the maximum number of recents entries that we will maintain and show. * @hide @@ -2750,30 +2761,6 @@ public class ActivityManager { } } - /** - * Updates (grants or revokes) a persitable URI permission. - * - * @param uri URI to be granted or revoked. - * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it - * applies to all URIs that are prefixed by this URI. - * @param packageName target package. - * @param grant if {@code true} a new permission will be granted, otherwise an existing - * permission will be revoked. - * - * @return whether or not the requested succeeded. - * - * @hide - */ - public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName, - boolean grant) { - try { - return getService().updatePersistableUriPermission(uri, prefix, packageName, grant, - UserHandle.myUserId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * Information you can retrieve about any processes that are in an error condition. */ @@ -3334,6 +3321,28 @@ public class ActivityManager { } } + /** + * Query whether the user has enabled background restrictions for this app. + * + *

The user may chose to do this, if they see that an app is consuming an unreasonable + * amount of battery while in the background.

+ * + *

If true, any work that the app tries to do will be aggressively restricted while it is in + * the background. At a minimum, jobs and alarms will not execute and foreground services + * cannot be started unless an app activity is in the foreground.

+ * + *

Note that these restrictions stay in effect even when the device is charging.

+ * + * @return true if user has enforced background restrictions for this app, false otherwise. + */ + public boolean isBackgroundRestricted() { + try { + return getService().isBackgroundRestricted(mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Sets the memory trim mode for a process and schedules a memory trim operation. * diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index feeb0f23e751bed75d8901cfe8ad4eba3acb3f5b..cab6744e3c81873237a6db8f8ec968e8925c234a 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.SystemClock; import android.service.voice.IVoiceInteractionSession; import android.util.SparseIntArray; +import android.view.RemoteAnimationAdapter; import com.android.internal.app.IVoiceInteractor; @@ -263,6 +264,17 @@ public abstract class ActivityManagerInternal { */ public abstract void setHasOverlayUi(int pid, boolean hasOverlayUi); + /** + * Sets if the given pid is currently running a remote animation, which is taken a signal for + * determining oom adjustment and scheduling behavior. + * + * @param pid The pid we are setting overlay UI for. + * @param runningRemoteAnimation True if the process is running a remote animation, false + * otherwise. + * @see RemoteAnimationAdapter + */ + public abstract void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation); + /** * Called after the network policy rules are updated by * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} and @@ -352,6 +364,11 @@ public abstract class ActivityManagerInternal { */ public abstract boolean isCallerRecents(int callingUid); + /** + * Returns whether the recents component is the home activity for the given user. + */ + public abstract boolean isRecentsComponentHomeActivity(int userId); + /** * Whether an UID is active or idle. */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4754588cbd27b9b3c66667cdfbadee1fac160c2a..1253416970a8968a71fb09ea393dfc9ada36efab 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -143,7 +143,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.org.conscrypt.OpenSSLSocketImpl; import com.android.org.conscrypt.TrustedCertificateStore; -import com.android.server.am.proto.MemInfoProto; +import com.android.server.am.proto.MemInfoDumpProto; import dalvik.system.BaseDexClassLoader; import dalvik.system.CloseGuard; @@ -1251,55 +1251,62 @@ public final class ActivityThread extends ClientTransactionHandler { long parcelCount = Parcel.getGlobalAllocCount(); SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); - final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY); - proto.write(MemInfoProto.ProcessMemory.PID, Process.myPid()); - proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, + final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY); + proto.write(MemInfoDumpProto.ProcessMemory.PID, Process.myPid()); + proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, (mBoundApplication != null) ? mBoundApplication.processName : "unknown"); dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly, nativeMax, nativeAllocated, nativeFree, dalvikMax, dalvikAllocated, dalvikFree); proto.end(mToken); - final long oToken = proto.start(MemInfoProto.AppData.OBJECTS); - proto.write(MemInfoProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, viewInstanceCount); - proto.write(MemInfoProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT, + final long oToken = proto.start(MemInfoDumpProto.AppData.OBJECTS); + proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, + viewInstanceCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT, viewRootInstanceCount); - proto.write(MemInfoProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT, appContextInstanceCount); - proto.write(MemInfoProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT, activityInstanceCount); - proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, globalAssetCount); - proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, + globalAssetCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT, globalAssetManagerCount); - proto.write(MemInfoProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT, binderLocalObjectCount); - proto.write(MemInfoProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT, binderProxyObjectCount); - proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_MEMORY_KB, parcelSize / 1024); - proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount); - proto.write(MemInfoProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_MEMORY_KB, + parcelSize / 1024); + proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT, binderDeathObjectCount); - proto.write(MemInfoProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT, openSslSocketCount); - proto.write(MemInfoProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT, + proto.write(MemInfoDumpProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT, + openSslSocketCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT, webviewInstanceCount); proto.end(oToken); // SQLite mem info - final long sToken = proto.start(MemInfoProto.AppData.SQL); - proto.write(MemInfoProto.AppData.SqlStats.MEMORY_USED_KB, stats.memoryUsed / 1024); - proto.write(MemInfoProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB, + final long sToken = proto.start(MemInfoDumpProto.AppData.SQL); + proto.write(MemInfoDumpProto.AppData.SqlStats.MEMORY_USED_KB, + stats.memoryUsed / 1024); + proto.write(MemInfoDumpProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB, stats.pageCacheOverflow / 1024); - proto.write(MemInfoProto.AppData.SqlStats.MALLOC_SIZE_KB, stats.largestMemAlloc / 1024); + proto.write(MemInfoDumpProto.AppData.SqlStats.MALLOC_SIZE_KB, + stats.largestMemAlloc / 1024); int n = stats.dbStats.size(); for (int i = 0; i < n; i++) { DbStats dbStats = stats.dbStats.get(i); - final long dToken = proto.start(MemInfoProto.AppData.SqlStats.DATABASES); - proto.write(MemInfoProto.AppData.SqlStats.Database.NAME, dbStats.dbName); - proto.write(MemInfoProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize); - proto.write(MemInfoProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize); - proto.write(MemInfoProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside); - proto.write(MemInfoProto.AppData.SqlStats.Database.CACHE, dbStats.cache); + final long dToken = proto.start(MemInfoDumpProto.AppData.SqlStats.DATABASES); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.NAME, dbStats.dbName); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B, + dbStats.lookaside); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE, dbStats.cache); proto.end(dToken); } proto.end(sToken); @@ -1307,7 +1314,7 @@ public final class ActivityThread extends ClientTransactionHandler { // Asset details. String assetAlloc = AssetManager.getAssetAllocations(); if (assetAlloc != null) { - proto.write(MemInfoProto.AppData.ASSET_ALLOCATIONS, assetAlloc); + proto.write(MemInfoDumpProto.AppData.ASSET_ALLOCATIONS, assetAlloc); } // Unreachable native memory @@ -1315,7 +1322,7 @@ public final class ActivityThread extends ClientTransactionHandler { int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags; boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 || android.os.Build.IS_DEBUGGABLE; - proto.write(MemInfoProto.AppData.UNREACHABLE_MEMORY, + proto.write(MemInfoDumpProto.AppData.UNREACHABLE_MEMORY, Debug.getUnreachableMemory(100, showContents)); } } @@ -2505,17 +2512,17 @@ public final class ActivityThread extends ClientTransactionHandler { boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss) { final long token = proto.start(fieldId); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.NAME, name); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean); - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.NAME, name); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean); if (hasSwappedOutPss) { - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss); } else { - proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap); + proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap); } proto.end(token); @@ -2530,26 +2537,26 @@ public final class ActivityThread extends ClientTransactionHandler { long dalvikMax, long dalvikAllocated, long dalvikFree) { if (!dumpSummaryOnly) { - final long nhToken = proto.start(MemInfoProto.ProcessMemory.NATIVE_HEAP); - dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap", + final long nhToken = proto.start(MemInfoDumpProto.ProcessMemory.NATIVE_HEAP); + dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap", memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, memInfo.nativePrivateDirty, memInfo.nativeSharedClean, memInfo.nativePrivateClean, memInfo.hasSwappedOutPss, memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree); proto.end(nhToken); - final long dvToken = proto.start(MemInfoProto.ProcessMemory.DALVIK_HEAP); - dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap", + final long dvToken = proto.start(MemInfoDumpProto.ProcessMemory.DALVIK_HEAP); + dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap", memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss, memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree); proto.end(dvToken); int otherPss = memInfo.otherPss; @@ -2573,7 +2580,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 || mySharedClean != 0 || myPrivateClean != 0 || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { - dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.OTHER_HEAPS, + dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.OTHER_HEAPS, Debug.MemoryInfo.getOtherLabel(i), myPss, mySwappablePss, mySharedDirty, myPrivateDirty, mySharedClean, myPrivateClean, @@ -2590,21 +2597,23 @@ public final class ActivityThread extends ClientTransactionHandler { } } - dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.UNKNOWN_HEAP, "Unknown", + dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.UNKNOWN_HEAP, "Unknown", otherPss, otherSwappablePss, otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss); - final long tToken = proto.start(MemInfoProto.ProcessMemory.TOTAL_HEAP); - dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL", + final long tToken = proto.start(MemInfoDumpProto.ProcessMemory.TOTAL_HEAP); + dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL", memInfo.getTotalPss(), memInfo.getTotalSwappablePss(), memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(), memInfo.getTotalSwappedOutPss()); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, + nativeMax + dalvikMax); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated + dalvikAllocated); - proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree); + proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, + nativeFree + dalvikFree); proto.end(tToken); if (dumpDalvik) { @@ -2622,7 +2631,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 || mySharedClean != 0 || myPrivateClean != 0 || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { - dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.DALVIK_DETAILS, + dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.DALVIK_DETAILS, Debug.MemoryInfo.getOtherLabel(i), myPss, mySwappablePss, mySharedDirty, myPrivateDirty, mySharedClean, myPrivateClean, @@ -2632,24 +2641,26 @@ public final class ActivityThread extends ClientTransactionHandler { } } - final long asToken = proto.start(MemInfoProto.ProcessMemory.APP_SUMMARY); - proto.write(MemInfoProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB, + final long asToken = proto.start(MemInfoDumpProto.ProcessMemory.APP_SUMMARY); + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB, memInfo.getSummaryJavaHeap()); - proto.write(MemInfoProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB, + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB, memInfo.getSummaryNativeHeap()); - proto.write(MemInfoProto.ProcessMemory.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode()); - proto.write(MemInfoProto.ProcessMemory.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack()); - proto.write(MemInfoProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB, + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.CODE_PSS_KB, + memInfo.getSummaryCode()); + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.STACK_PSS_KB, + memInfo.getSummaryStack()); + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB, memInfo.getSummaryGraphics()); - proto.write(MemInfoProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB, + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB, memInfo.getSummaryPrivateOther()); - proto.write(MemInfoProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB, + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB, memInfo.getSummarySystem()); if (memInfo.hasSwappedOutPss) { - proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS, + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS, memInfo.getSummaryTotalSwapPss()); } else { - proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS, + proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS, memInfo.getSummaryTotalSwap()); } proto.end(asToken); @@ -3716,6 +3727,10 @@ public final class ActivityThread extends ClientTransactionHandler { if (localLOGV) Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished); if (r != null && !r.activity.mFinished) { + if (r.getLifecycleState() == ON_RESUME) { + throw new IllegalStateException( + "Trying to resume activity which is already resumed"); + } if (clearHide) { r.hideForNow = false; r.activity.mStartedActivity = false; @@ -3900,8 +3915,7 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, - int configChanges, boolean dontReport, PendingTransactionActions pendingActions, - String reason) { + int configChanges, PendingTransactionActions pendingActions, String reason) { ActivityClientRecord r = mActivities.get(token); if (r != null) { if (userLeaving) { @@ -3915,15 +3929,6 @@ public final class ActivityThread extends ClientTransactionHandler { if (r.isPreHoneycomb()) { QueuedWork.waitToFinish(); } - - // Tell the activity manager we have paused. - if (!dontReport) { - try { - ActivityManager.getService().activityPaused(token); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } mSomeActivitiesChanged = true; } } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index c5b3a4acd339fd96064c6314b8711cfd035d32a4..05a9861f5a20b47564c731e72d23ceeceebcd77d 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -31,7 +31,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; -import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; @@ -168,12 +167,14 @@ public class AppOpsManager { /** @hide */ public static final int OP_WRITE_SETTINGS = 23; /** @hide Required to draw on top of other apps. */ + @TestApi public static final int OP_SYSTEM_ALERT_WINDOW = 24; /** @hide */ public static final int OP_ACCESS_NOTIFICATIONS = 25; /** @hide */ public static final int OP_CAMERA = 26; /** @hide */ + @TestApi public static final int OP_RECORD_AUDIO = 27; /** @hide */ public static final int OP_PLAY_AUDIO = 28; @@ -1540,6 +1541,7 @@ public class AppOpsManager { * * @hide */ + @TestApi public interface OnOpActiveChangedListener { /** * Called when the active state of an app op changes. @@ -1731,15 +1733,14 @@ public class AppOpsManager { * Monitor for changes to the operating mode for the given op in the given app package. * *

If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission - * to watch changes only for your UID. + * you can watch changes only for your UID. * * @param op The operation to monitor, one of OP_*. * @param packageName The name of the application to monitor. * @param callback Where to report changes. * @hide */ - // TODO: Uncomment below annotation once b/73559440 is fixed - // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) + @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); @@ -1788,6 +1789,9 @@ public class AppOpsManager { * watched ops for a registered callback you need to unregister and register it * again. * + *

If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission + * you can watch changes only for your UID. + * * @param ops The ops to watch. * @param callback Where to report changes. * @@ -1798,7 +1802,9 @@ public class AppOpsManager { * * @hide */ - @RequiresPermission(Manifest.permission.WATCH_APPOPS) + @TestApi + // TODO: Uncomment below annotation once b/73559440 is fixed + // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingActive(@NonNull int[] ops, @NonNull OnOpActiveChangedListener callback) { Preconditions.checkNotNull(ops, "ops cannot be null"); @@ -1836,6 +1842,7 @@ public class AppOpsManager { * * @hide */ + @TestApi public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) { synchronized (mActiveWatchers) { final IAppOpsActiveCallback cb = mActiveWatchers.get(callback); @@ -2087,15 +2094,11 @@ public class AppOpsManager { * @hide */ public int noteOp(int op, int uid, String packageName) { - try { - int mode = mService.noteOperation(op, uid, packageName); - if (mode == MODE_ERRORED) { - throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); - } - return mode; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + final int mode = noteOpNoThrow(op, uid, packageName); + if (mode == MODE_ERRORED) { + throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } + return mode; } /** @@ -2174,6 +2177,11 @@ public class AppOpsManager { } } + /** @hide */ + public int startOp(int op) { + return startOp(op, Process.myUid(), mContext.getOpPackageName()); + } + /** * Report that an application has started executing a long-running operation. Note that you * must pass in both the uid and name of the application to be checked; this function will @@ -2182,6 +2190,7 @@ public class AppOpsManager { * the current time and the operation will be marked as "running". In this case you must * later call {@link #finishOp(int, int, String)} to report when the application is no * longer performing the operation. + * * @param op The operation to start. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. @@ -2192,15 +2201,34 @@ public class AppOpsManager { * @hide */ public int startOp(int op, int uid, String packageName) { - try { - int mode = mService.startOperation(getToken(mService), op, uid, packageName); - if (mode == MODE_ERRORED) { - throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); - } - return mode; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + return startOp(op, uid, packageName, false); + } + + /** + * Report that an application has started executing a long-running operation. Similar + * to {@link #startOp(String, int, String) except that if the mode is {@link #MODE_DEFAULT} + * the operation should succeed since the caller has performed its standard permission + * checks which passed and would perform the protected operation for this mode. + * + * @param op The operation to start. One of the OP_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}. + * + * @throws SecurityException If the app has been configured to crash on this op or + * the package is not in the passed in UID. + * + * @hide + */ + public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) { + final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault); + if (mode == MODE_ERRORED) { + throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } + return mode; } /** @@ -2209,18 +2237,32 @@ public class AppOpsManager { * @hide */ public int startOpNoThrow(int op, int uid, String packageName) { + return startOpNoThrow(op, uid, packageName, false); + } + + /** + * Like {@link #startOp(int, int, String, boolean)} but instead of throwing a + * {@link SecurityException} it returns {@link #MODE_ERRORED}. + * + * @param op The operation to start. One of the OP_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}. + * + * @hide + */ + public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) { try { - return mService.startOperation(getToken(mService), op, uid, packageName); + return mService.startOperation(getToken(mService), op, uid, packageName, + startIfModeDefault); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - /** @hide */ - public int startOp(int op) { - return startOp(op, Process.myUid(), mContext.getOpPackageName()); - } - /** * Report that an application is no longer performing an operation that had previously * been started with {@link #startOp(int, int, String)}. There is no validation of input @@ -2241,8 +2283,21 @@ public class AppOpsManager { finishOp(op, Process.myUid(), mContext.getOpPackageName()); } - /** @hide */ - @RequiresPermission(Manifest.permission.WATCH_APPOPS) + /** + * Checks whether the given op for a UID and package is active. + * + *

If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission + * you can query only for your UID. + * + * @see #startWatchingActive(int[], OnOpActiveChangedListener) + * @see #stopWatchingMode(OnOpChangedListener) + * @see #finishOp(int) + * @see #startOp(int) + * + * @hide */ + @TestApi + // TODO: Uncomment below annotation once b/73559440 is fixed + // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public boolean isOperationActive(int code, int uid, String packageName) { try { return mService.isOperationActive(code, uid, packageName); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index f8f50a2785110c664d06c6d303351dff529235a1..21fb18a212a8e1ba764e25861b62dd63386bcfa5 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1354,11 +1354,10 @@ public class ApplicationPackageManager extends PackageManager { if (badgeColor == null) { return null; } - badgeColor.setTint(getUserBadgeColor(user)); Drawable badgeForeground = getDrawableForDensity( com.android.internal.R.drawable.ic_corp_badge_case, density); - Drawable badge = new LayerDrawable( - new Drawable[] {badgeColor, badgeForeground }); + badgeForeground.setTint(getUserBadgeColor(user)); + Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground }); return badge; } diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index cb52a855e7ca128fb554331281a8536178b7da59..6bc66ecdb2618e89f913bbc7f31045ba259d2f2e 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -65,8 +65,7 @@ public abstract class ClientTransactionHandler { /** Pause the activity. */ public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, - int configChanges, boolean dontReport, PendingTransactionActions pendingActions, - String reason); + int configChanges, PendingTransactionActions pendingActions, String reason); /** Resume the activity. */ public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 99fb465f5c41b26d982026282c97ad1a7a939140..f5e138c164b69e61619639a6276e0c6f1f878a8b 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -92,6 +92,7 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; class ReceiverRestrictedContext extends ContextWrapper { ReceiverRestrictedContext(Context base) { @@ -208,6 +209,17 @@ class ContextImpl extends Context { // The system service cache for the system services that are cached per-ContextImpl. final Object[] mServiceCache = SystemServiceRegistry.createServiceCache(); + static final int STATE_UNINITIALIZED = 0; + static final int STATE_INITIALIZING = 1; + static final int STATE_READY = 2; + + /** + * Initialization state for each service. Any of {@link #STATE_UNINITIALIZED}, + * {@link #STATE_INITIALIZING} or {@link #STATE_READY}, + */ + final AtomicInteger[] mServiceInitializationStateArray = + SystemServiceRegistry.createServiceInitializationStateArray(); + static ContextImpl getImpl(Context context) { Context nextContext; while ((context instanceof ContextWrapper) && diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 4ff07f2edbf5575919115c98fc9b906355d93a61..8e8270a0890f09514664737d81abd7d547abc155 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -2311,7 +2311,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * transaction have called {@link #startPostponedEnterTransition()}. *

* This method should be called before being added to the FragmentTransaction or - * in {@link #onCreate(Bundle), {@link #onAttach(Context)}, or + * in {@link #onCreate(Bundle)}, {@link #onAttach(Context)}, or * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}}. * {@link #startPostponedEnterTransition()} must be called to allow the Fragment to * start the transitions. diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index ac301b3cc59550318fde302a6c4352dd0af5f737..0d45dfa9c7bf98abf9fe2251f7777e89bb4d16a8 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -423,10 +423,8 @@ interface IActivityManager { void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle); void restart(); void performIdleMaintenance(); - void takePersistableUriPermission(in Uri uri, int modeFlags, int userId); - boolean updatePersistableUriPermission(in Uri uri, boolean prefix, String packageName, - boolean grant, int userId); - void releasePersistableUriPermission(in Uri uri, int modeFlags, int userId); + void takePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId); + void releasePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId); ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming); void appNotRespondingViaProvider(in IBinder connection); Rect getTaskBounds(int taskId); @@ -614,7 +612,7 @@ interface IActivityManager { int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code, in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver, in String requiredPermission, in Bundle options); - + boolean isBackgroundRestricted(in String packageName); // Start of N MR1 transactions void setVrThread(int tid); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index d378f227d921da7b96fc42531d78fe7d3f545d0a..ddd065692ea2af277f34474b53b2912436f870d2 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -45,6 +45,8 @@ interface INotificationManager void clearData(String pkg, int uid, boolean fromApp); void enqueueToast(String pkg, ITransientNotification callback, int duration); void cancelToast(String pkg, ITransientNotification callback); + void finishToken(String pkg, ITransientNotification callback); + void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, in Notification notification, int userId); void cancelNotificationWithTag(String pkg, String tag, int id, int userId); diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index 2ba4c00c3f469f23e73a7a101037722bbfc795e9..58d0aaf5b621db1c12737554ea380b835d5a3d82 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -62,7 +62,7 @@ public abstract class InstantAppResolverService extends Service { @Deprecated public void onGetInstantAppResolveInfo( int digestPrefix[], String token, InstantAppResolutionCallback callback) { - throw new IllegalStateException("Must define"); + throw new IllegalStateException("Must define onGetInstantAppResolveInfo"); } /** @@ -80,10 +80,26 @@ public abstract class InstantAppResolverService extends Service { } /** - * Called to retrieve resolve info for instant applications immediately. + * Called to retrieve resolve info for instant applications immediately. The response will be + * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided + * in response to this method may be partial to request a second phase of resolution which will + * result in a subsequent call to + * {@link #onGetInstantAppIntentFilter(Intent, int[], String, InstantAppResolutionCallback)} * - * @param sanitizedIntent The sanitized {@link Intent} used for resolution. + * + * @param sanitizedIntent The sanitized {@link Intent} used for resolution. A sanitized Intent + * is an intent with potential PII removed from the original intent. + * Fields removed include extras and the host + path of the data, if + * defined. * @param hostDigestPrefix The hash prefix of the instant app's domain. + * @param token A unique identifier that will be provided in calls to + * {@link #onGetInstantAppIntentFilter(Intent, int[], String, + * InstantAppResolutionCallback)} + * and provided to the installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN} to + * tie a single launch together. + * @param callback The {@link InstantAppResolutionCallback} to provide results to. + * + * @see InstantAppResolveInfo */ public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix, String token, InstantAppResolutionCallback callback) { @@ -96,12 +112,20 @@ public abstract class InstantAppResolverService extends Service { } /** - * Called to retrieve intent filters for instant applications from potentially expensive - * sources. + * Called to retrieve intent filters for potentially matching instant applications. Unlike + * {@link #onGetInstantAppResolveInfo(Intent, int[], String, InstantAppResolutionCallback)}, + * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s + * provided in response to this method must be completely populated. * * @param sanitizedIntent The sanitized {@link Intent} used for resolution. * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is * defined. + * @param token A unique identifier that was provided in + * {@link #onGetInstantAppResolveInfo(Intent, int[], String, + * InstantAppResolutionCallback)} + * and provided to the currently visible installer via + * {@link Intent#EXTRA_INSTANT_APP_TOKEN}. + * @param callback The {@link InstantAppResolutionCallback} to provide results to */ public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix, String token, InstantAppResolutionCallback callback) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index ea5932cd4ae4151a324ed2768eaadb467ebbee94..fc7d9a553e3173a4df0c7df8ec53ca9edecd7830 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -219,7 +219,7 @@ public final class LoadedApk { } private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) { - if (appInfo.appComponentFactory != null) { + if (appInfo.appComponentFactory != null && cl != null) { try { return (AppComponentFactory) cl.loadClass(appInfo.appComponentFactory) .newInstance(); @@ -613,6 +613,7 @@ public final class LoadedApk { } else { mClassLoader = ClassLoader.getSystemClassLoader(); } + mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader); return; } @@ -687,6 +688,7 @@ public final class LoadedApk { librarySearchPath, libraryPermittedPath, mBaseClassLoader, null /* classLoaderName */); StrictMode.setThreadPolicy(oldPolicy); + mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader); } return; @@ -714,6 +716,7 @@ public final class LoadedApk { mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, mBaseClassLoader, mApplicationInfo.classLoaderName); + mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader); StrictMode.setThreadPolicy(oldPolicy); // Setup the class loader paths for profiling. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 233e09d90f9b7bdf4485c6e45c837fe9a4878c5d..13a6be557dae6f180e4ed248d81ea5950d92e53a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -360,6 +360,23 @@ public class Notification implements Parcelable @Deprecated public RemoteViews headsUpContentView; + private boolean mUsesStandardHeader; + + private static final ArraySet STANDARD_LAYOUTS = new ArraySet<>(); + static { + STANDARD_LAYOUTS.add(R.layout.notification_template_material_base); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_base); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_picture); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_media); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media); + STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header); + STANDARD_LAYOUTS.add(R.layout.notification_template_header); + STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient); + } + /** * A large bitmap to be shown in the notification content area. * @@ -2534,6 +2551,8 @@ public class Notification implements Parcelable } parcel.writeInt(mGroupAlertBehavior); + + // mUsesStandardHeader is not written because it should be recomputed in listeners } /** @@ -4092,6 +4111,25 @@ public class Notification implements Parcelable } } + /** + * @hide + */ + public boolean usesStandardHeader() { + if (mN.mUsesStandardHeader) { + return true; + } + if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) { + if (mN.contentView == null && mN.bigContentView == null) { + return true; + } + } + boolean contentViewUsesHeader = mN.contentView == null + || STANDARD_LAYOUTS.contains(mN.contentView.getLayoutId()); + boolean bigContentViewUsesHeader = mN.bigContentView == null + || STANDARD_LAYOUTS.contains(mN.bigContentView.getLayoutId()); + return contentViewUsesHeader && bigContentViewUsesHeader; + } + private void resetStandardTemplate(RemoteViews contentView) { resetNotificationHeader(contentView); resetContentMargins(contentView); @@ -4123,6 +4161,7 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.time, View.GONE); contentView.setImageViewIcon(R.id.profile_badge, null); contentView.setViewVisibility(R.id.profile_badge, View.GONE); + mN.mUsesStandardHeader = false; } private void resetContentMargins(RemoteViews contentView) { @@ -4444,6 +4483,7 @@ public class Notification implements Parcelable bindProfileBadge(contentView); } bindExpandButton(contentView); + mN.mUsesStandardHeader = true; } private void bindExpandButton(RemoteViews contentView) { diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index b10e60870721a9c447e79aa3e8a11cfe409ede4a..46d1264f952d2cd4a314fe5411623f0efb0423f9 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1031,12 +1031,18 @@ public class NotificationManager { public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4; /** Alarms are prioritized */ public static final int PRIORITY_CATEGORY_ALARMS = 1 << 5; - /** Media, system, game (catch-all for non-never suppressible sounds) are prioritized */ - public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 1 << 6; + /** Media, game, voice navigation are prioritized */ + public static final int PRIORITY_CATEGORY_MEDIA = 1 << 6; + /**System (catch-all for non-never suppressible sounds) are prioritized */ + public static final int PRIORITY_CATEGORY_SYSTEM = 1 << 7; - private static final int[] ALL_PRIORITY_CATEGORIES = { + /** + * @hide + */ + public static final int[] ALL_PRIORITY_CATEGORIES = { PRIORITY_CATEGORY_ALARMS, - PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, + PRIORITY_CATEGORY_MEDIA, + PRIORITY_CATEGORY_SYSTEM, PRIORITY_CATEGORY_REMINDERS, PRIORITY_CATEGORY_EVENTS, PRIORITY_CATEGORY_MESSAGES, @@ -1066,20 +1072,77 @@ public class NotificationManager { * @hide */ public static final int SUPPRESSED_EFFECTS_UNSET = -1; + /** * Whether notifications suppressed by DND should not interrupt visually (e.g. with * notification lights or by turning the screen on) when the screen is off. + * + * @deprecated use {@link #SUPPRESSED_EFFECT_FULL_SCREEN_INTENT} and + * {@link #SUPPRESSED_EFFECT_AMBIENT} and {@link #SUPPRESSED_EFFECT_LIGHTS} individually. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1 << 0; /** * Whether notifications suppressed by DND should not interrupt visually when the screen * is on (e.g. by peeking onto the screen). + * + * @deprecated use {@link #SUPPRESSED_EFFECT_PEEK}. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 1; + /** + * Whether {@link Notification#fullScreenIntent full screen intents} from + * notifications intercepted by DND are blocked. + */ + public static final int SUPPRESSED_EFFECT_FULL_SCREEN_INTENT = 1 << 2; + + /** + * Whether {@link NotificationChannel#shouldShowLights() notification lights} from + * notifications intercepted by DND are blocked. + */ + public static final int SUPPRESSED_EFFECT_LIGHTS = 1 << 3; + + /** + * Whether notifications intercepted by DND are prevented from peeking. + */ + public static final int SUPPRESSED_EFFECT_PEEK = 1 << 4; + + /** + * Whether notifications intercepted by DND are prevented from appearing in the status bar, + * on devices that support status bars. + */ + public static final int SUPPRESSED_EFFECT_STATUS_BAR = 1 << 5; + + /** + * Whether {@link NotificationChannel#canShowBadge() badges} from + * notifications intercepted by DND are blocked on devices that support badging. + */ + public static final int SUPPRESSED_EFFECT_BADGE = 1 << 6; + + /** + * Whether notification intercepted by DND are prevented from appearing on ambient displays + * on devices that support ambient display. + */ + public static final int SUPPRESSED_EFFECT_AMBIENT = 1 << 7; + + /** + * Whether notification intercepted by DND are prevented from appearing in notification + * list views like the notification shade or lockscreen on devices that support those + * views. + */ + public static final int SUPPRESSED_EFFECT_NOTIFICATION_LIST = 1 << 8; + private static final int[] ALL_SUPPRESSED_EFFECTS = { SUPPRESSED_EFFECT_SCREEN_OFF, SUPPRESSED_EFFECT_SCREEN_ON, + SUPPRESSED_EFFECT_FULL_SCREEN_INTENT, + SUPPRESSED_EFFECT_LIGHTS, + SUPPRESSED_EFFECT_PEEK, + SUPPRESSED_EFFECT_STATUS_BAR, + SUPPRESSED_EFFECT_BADGE, + SUPPRESSED_EFFECT_AMBIENT, + SUPPRESSED_EFFECT_NOTIFICATION_LIST }; /** @@ -1091,6 +1154,12 @@ public class NotificationManager { /** * Constructs a policy for Do Not Disturb priority mode behavior. * + *

+ * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot + * change user-designated values to allow or disallow + * {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and + * {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd. + * * @param priorityCategories bitmask of categories of notifications that can bypass DND. * @param priorityCallSenders which callers can bypass DND. * @param priorityMessageSenders which message senders can bypass DND. @@ -1103,6 +1172,26 @@ public class NotificationManager { /** * Constructs a policy for Do Not Disturb priority mode behavior. * + *

+ * Apps that target API levels below {@link Build.VERSION_CODES#P} cannot + * change user-designated values to allow or disallow + * {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and + * {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd. + *

+ * Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can + * only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and + * {@link #SUPPRESSED_EFFECT_SCREEN_OFF} bits of the suppressed visual effects field. + * All other suppressed effects will be ignored and reconstituted from the screen on + * and screen off values. + *

+ * Apps that target {@link Build.VERSION_CODES#P} or above can set any + * suppressed visual effects. However, if any suppressed effects > + * {@link #SUPPRESSED_EFFECT_SCREEN_ON} are set, {@link #SUPPRESSED_EFFECT_SCREEN_ON} + * and {@link #SUPPRESSED_EFFECT_SCREEN_OFF} will be ignored and reconstituted from + * the more specific suppressed visual effect bits. Apps should migrate to targeting + * specific effects instead of the deprecated {@link #SUPPRESSED_EFFECT_SCREEN_ON} and + * {@link #SUPPRESSED_EFFECT_SCREEN_OFF} effects. + * * @param priorityCategories bitmask of categories of notifications that can bypass DND. * @param priorityCallSenders which callers can bypass DND. * @param priorityMessageSenders which message senders can bypass DND. @@ -1184,6 +1273,30 @@ public class NotificationManager { } } + /** + * @hide + */ + public static int getAllSuppressedVisualEffects() { + int effects = 0; + for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) { + effects |= ALL_SUPPRESSED_EFFECTS[i]; + } + return effects; + } + + /** + * @hide + */ + public static boolean areAllVisualEffectsSuppressed(int effects) { + for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) { + final int effect = ALL_SUPPRESSED_EFFECTS[i]; + if ((effects & effect) == 0) { + return false; + } + } + return true; + } + public static String suppressedEffectsToString(int effects) { if (effects <= 0) return ""; final StringBuilder sb = new StringBuilder(); @@ -1222,9 +1335,26 @@ public class NotificationManager { private static String effectToString(int effect) { switch (effect) { - case SUPPRESSED_EFFECT_SCREEN_OFF: return "SUPPRESSED_EFFECT_SCREEN_OFF"; - case SUPPRESSED_EFFECT_SCREEN_ON: return "SUPPRESSED_EFFECT_SCREEN_ON"; - case SUPPRESSED_EFFECTS_UNSET: return "SUPPRESSED_EFFECTS_UNSET"; + case SUPPRESSED_EFFECT_FULL_SCREEN_INTENT: + return "SUPPRESSED_EFFECT_FULL_SCREEN_INTENT"; + case SUPPRESSED_EFFECT_LIGHTS: + return "SUPPRESSED_EFFECT_LIGHTS"; + case SUPPRESSED_EFFECT_PEEK: + return "SUPPRESSED_EFFECT_PEEK"; + case SUPPRESSED_EFFECT_STATUS_BAR: + return "SUPPRESSED_EFFECT_STATUS_BAR"; + case SUPPRESSED_EFFECT_BADGE: + return "SUPPRESSED_EFFECT_BADGE"; + case SUPPRESSED_EFFECT_AMBIENT: + return "SUPPRESSED_EFFECT_AMBIENT"; + case SUPPRESSED_EFFECT_NOTIFICATION_LIST: + return "SUPPRESSED_EFFECT_NOTIFICATION_LIST"; + case SUPPRESSED_EFFECT_SCREEN_OFF: + return "SUPPRESSED_EFFECT_SCREEN_OFF"; + case SUPPRESSED_EFFECT_SCREEN_ON: + return "SUPPRESSED_EFFECT_SCREEN_ON"; + case SUPPRESSED_EFFECTS_UNSET: + return "SUPPRESSED_EFFECTS_UNSET"; default: return "UNKNOWN_" + effect; } } @@ -1237,8 +1367,8 @@ public class NotificationManager { case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS"; case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS"; case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS"; - case PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER: - return "PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER"; + case PRIORITY_CATEGORY_MEDIA: return "PRIORITY_CATEGORY_MEDIA"; + case PRIORITY_CATEGORY_SYSTEM: return "PRIORITY_CATEGORY_SYSTEM"; default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory; } } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index fb11272d7e622e06891c75195ed2773951ea8e10..fc5ea6607d87b2e3cecb0e579d657d2e46815876 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -21,6 +21,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; @@ -34,6 +35,7 @@ import android.os.Trace; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; +import android.util.LruCache; import android.util.Pair; import android.util.Slog; import android.view.Display; @@ -41,9 +43,13 @@ import android.view.DisplayAdjustments; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Predicate; @@ -59,12 +65,7 @@ public class ResourcesManager { * Predicate that returns true if a WeakReference is gc'ed. */ private static final Predicate> sEmptyReferencePredicate = - new Predicate>() { - @Override - public boolean test(WeakReference weakRef) { - return weakRef == null || weakRef.get() == null; - } - }; + weakRef -> weakRef == null || weakRef.get() == null; /** * The global compatibility settings. @@ -89,6 +90,48 @@ public class ResourcesManager { */ private final ArrayList> mResourceReferences = new ArrayList<>(); + private static class ApkKey { + public final String path; + public final boolean sharedLib; + public final boolean overlay; + + ApkKey(String path, boolean sharedLib, boolean overlay) { + this.path = path; + this.sharedLib = sharedLib; + this.overlay = overlay; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + this.path.hashCode(); + result = 31 * result + Boolean.hashCode(this.sharedLib); + result = 31 * result + Boolean.hashCode(this.overlay); + return result; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ApkKey)) { + return false; + } + ApkKey other = (ApkKey) obj; + return this.path.equals(other.path) && this.sharedLib == other.sharedLib + && this.overlay == other.overlay; + } + } + + /** + * The ApkAssets we are caching and intend to hold strong references to. + */ + private final LruCache mLoadedApkAssets = new LruCache<>(15); + + /** + * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't + * in our LRU cache. Bonus resources :) + */ + private final ArrayMap> mCachedApkAssets = new ArrayMap<>(); + /** * Resources and base configuration override associated with an Activity. */ @@ -260,6 +303,43 @@ public class ResourcesManager { } } + private static String overlayPathToIdmapPath(String path) { + return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; + } + + private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay) + throws IOException { + final ApkKey newKey = new ApkKey(path, sharedLib, overlay); + ApkAssets apkAssets = mLoadedApkAssets.get(newKey); + if (apkAssets != null) { + return apkAssets; + } + + // Optimistically check if this ApkAssets exists somewhere else. + final WeakReference apkAssetsRef = mCachedApkAssets.get(newKey); + if (apkAssetsRef != null) { + apkAssets = apkAssetsRef.get(); + if (apkAssets != null) { + mLoadedApkAssets.put(newKey, apkAssets); + return apkAssets; + } else { + // Clean up the reference. + mCachedApkAssets.remove(newKey); + } + } + + // We must load this from disk. + if (overlay) { + apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), + false /*system*/); + } else { + apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib); + } + mLoadedApkAssets.put(newKey, apkAssets); + mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets)); + return apkAssets; + } + /** * Creates an AssetManager from the paths within the ResourcesKey. * @@ -270,13 +350,16 @@ public class ResourcesManager { */ @VisibleForTesting protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { - AssetManager assets = new AssetManager(); + final AssetManager.Builder builder = new AssetManager.Builder(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (key.mResDir != null) { - if (assets.addAssetPath(key.mResDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add asset path " + key.mResDir); return null; } @@ -284,7 +367,10 @@ public class ResourcesManager { if (key.mSplitResDirs != null) { for (final String splitResDir : key.mSplitResDirs) { - if (assets.addAssetPath(splitResDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add split asset path " + splitResDir); return null; } @@ -293,7 +379,14 @@ public class ResourcesManager { if (key.mOverlayDirs != null) { for (final String idmapPath : key.mOverlayDirs) { - assets.addOverlayPath(idmapPath); + try { + builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, + true /*overlay*/)); + } catch (IOException e) { + Log.w(TAG, "failed to add overlay path " + idmapPath); + + // continue. + } } } @@ -302,14 +395,73 @@ public class ResourcesManager { if (libDir.endsWith(".apk")) { // Avoid opening files we know do not have resources, // like code-only .jar files. - if (assets.addAssetPathAsSharedLibrary(libDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); + + // continue. } } } } - return assets; + + return builder.build(); + } + + private static int countLiveReferences(Collection> collection) { + int count = 0; + for (WeakReference ref : collection) { + final T value = ref != null ? ref.get() : null; + if (value != null) { + count++; + } + } + return count; + } + + /** + * @hide + */ + public void dump(String prefix, PrintWriter printWriter) { + synchronized (this) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); + for (int i = 0; i < prefix.length() / 2; i++) { + pw.increaseIndent(); + } + + pw.println("ResourcesManager:"); + pw.increaseIndent(); + pw.print("cached apks: total="); + pw.print(mLoadedApkAssets.size()); + pw.print(" created="); + pw.print(mLoadedApkAssets.createCount()); + pw.print(" evicted="); + pw.print(mLoadedApkAssets.evictionCount()); + pw.print(" hit="); + pw.print(mLoadedApkAssets.hitCount()); + pw.print(" miss="); + pw.print(mLoadedApkAssets.missCount()); + pw.print(" max="); + pw.print(mLoadedApkAssets.maxSize()); + pw.println(); + + pw.print("total apks: "); + pw.println(countLiveReferences(mCachedApkAssets.values())); + + pw.print("resources: "); + + int references = countLiveReferences(mResourceReferences); + for (ActivityResources activityResources : mActivityResourceReferences.values()) { + references += countLiveReferences(activityResources.activityResources); + } + pw.println(references); + + pw.print("resource impls: "); + pw.println(countLiveReferences(mResourceImpls.values())); + } } private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) { @@ -630,28 +782,16 @@ public class ResourcesManager { // We will create the ResourcesImpl object outside of holding this lock. } - } - - // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. - ResourcesImpl resourcesImpl = createResourcesImpl(key); - if (resourcesImpl == null) { - return null; - } - synchronized (this) { - ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); - if (existingResourcesImpl != null) { - if (DEBUG) { - Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl - + " new impl=" + resourcesImpl); - } - resourcesImpl.getAssets().close(); - resourcesImpl = existingResourcesImpl; - } else { - // Add this ResourcesImpl to the cache. - mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. + ResourcesImpl resourcesImpl = createResourcesImpl(key); + if (resourcesImpl == null) { + return null; } + // Add this ResourcesImpl to the cache. + mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + final Resources resources; if (activityToken != null) { resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 4e2cb646e714a4d0cb5d4c33f5a3f0292341f919..49faf4029157ed4c4033ef8284b374b8773b48e4 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -33,7 +33,6 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -752,6 +751,8 @@ public class SearchManager * *

This function can be safely called at any time (even if no search is active.) * + *

{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method. + * * @see #startSearch */ public void stopSearch() { @@ -802,6 +803,8 @@ public class SearchManager /** * Set or clear the callback that will be invoked whenever the search UI is dismissed. * + *

{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method. + * * @param listener The {@link OnDismissListener} to use, or null. */ public void setOnDismissListener(final OnDismissListener listener) { @@ -811,6 +814,8 @@ public class SearchManager /** * Set or clear the callback that will be invoked whenever the search UI is canceled. * + *

{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method. + * * @param listener The {@link OnCancelListener} to use, or null. */ public void setOnCancelListener(OnCancelListener listener) { diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 256c47934dc543d33de34d886497a1240b68bea8..ea0fd75bec9041c6c32648cd772a2497db232ffb 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -471,14 +471,6 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link #onStart} and returns either {@link #START_STICKY} * or {@link #START_STICKY_COMPATIBILITY}. * - *

If you need your application to run on platform versions prior to API - * level 5, you can use the following model to handle the older {@link #onStart} - * callback in that case. The handleCommand method is implemented by - * you as appropriate: - * - * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java - * start_compatibility} - * *

Note that the system calls this on your * service's main thread. A service's main thread is the same * thread where UI operations take place for Activities running in the @@ -687,6 +679,10 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link #startService(Intent)} first to tell the system it should keep the service running, * and then use this method to tell it to keep it running harder.

* + *

Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request + * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use + * this API.

+ * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index aa52cdef70c6a353361e4c6a9ae3849550a95ad4..1776eacecb49b9af86b73b922878953a01a56fe5 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -160,6 +160,7 @@ import com.android.internal.os.IDropBoxManagerService; import com.android.internal.policy.PhoneLayoutInflater; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicInteger; /** * Manages all of the system services that can be returned by {@link Context#getSystemService}. @@ -992,6 +993,10 @@ final class SystemServiceRegistry { return new Object[sServiceCacheSize]; } + public static AtomicInteger[] createServiceInitializationStateArray() { + return new AtomicInteger[sServiceCacheSize]; + } + /** * Gets a system service from a given context. */ @@ -1040,19 +1045,95 @@ final class SystemServiceRegistry { @SuppressWarnings("unchecked") public final T getService(ContextImpl ctx) { final Object[] cache = ctx.mServiceCache; + + // Fast path. If it's already cached, just return it. + Object service = cache[mCacheIndex]; + if (service != null) { + return (T) service; + } + + // Slow path. + final AtomicInteger[] gates = ctx.mServiceInitializationStateArray; + final AtomicInteger gate; + synchronized (cache) { - // Fetch or create the service. - Object service = cache[mCacheIndex]; - if (service == null) { + // See if it's cached or not again, with the lock held this time. + service = cache[mCacheIndex]; + if (service != null) { + return (T) service; + } + + // Not initialized yet. Create an atomic boolean to control which thread should + // instantiate the service. + if (gates[mCacheIndex] != null) { + gate = gates[mCacheIndex]; + } else { + gate = new AtomicInteger(ContextImpl.STATE_UNINITIALIZED); + gates[mCacheIndex] = gate; + } + } + + // Not cached yet. + // + // Note multiple threads can reach here for the same service on the same context + // concurrently. + // + // Now we're going to instantiate the service, but do so without the cache held; + // otherwise it could deadlock. (b/71882178) + // + // However we still don't want to instantiate the same service multiple times, so + // use the atomic integer to ensure only one thread will call createService(). + + if (gate.compareAndSet( + ContextImpl.STATE_UNINITIALIZED, ContextImpl.STATE_INITIALIZING)) { + try { + // This thread is the first one to get here. Instantiate the service + // *without* the cache lock held. try { service = createService(ctx); - cache[mCacheIndex] = service; + + synchronized (cache) { + cache[mCacheIndex] = service; + } } catch (ServiceNotFoundException e) { onServiceNotFound(e); } + } finally { + // Tell the all other threads that the cache is ready now. + // (But it's still be null in case of ServiceNotFoundException.) + synchronized (gate) { + gate.set(ContextImpl.STATE_READY); + gate.notifyAll(); + } } - return (T)service; + return (T) service; + } + // Other threads will wait on the gate lock. + synchronized (gate) { + boolean interrupted = false; + + // Note: We check whether "state == STATE_READY", not + // "cache[mCacheIndex] != null", because "cache[mCacheIndex] == null" + // is still a valid outcome in the ServiceNotFoundException case. + while (gate.get() != ContextImpl.STATE_READY) { + try { + gate.wait(); + } catch (InterruptedException e) { + Log.w(TAG, "getService() interrupted"); + interrupted = true; + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + // Now the first thread has initialized it. + // It may still be null if ServiceNotFoundException was thrown, but that shouldn't + // happen, so we'll just return null here in that case. + synchronized (cache) { + service = cache[mCacheIndex]; } + return (T) service; } public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException; diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index a2864b9d83af431ba1bcbff77af23f6852f9fc30..2d007adf86e6b665dfecfc426fc1420977de8a2f 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -107,6 +108,11 @@ public final class WallpaperColors implements Parcelable { * @param drawable Source where to extract from. */ public static WallpaperColors fromDrawable(Drawable drawable) { + if (drawable == null) { + throw new IllegalArgumentException("Drawable cannot be null"); + } + + Rect initialBounds = drawable.copyBounds(); int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); @@ -126,6 +132,7 @@ public final class WallpaperColors implements Parcelable { final WallpaperColors colors = WallpaperColors.fromBitmap(bitmap); bitmap.recycle(); + drawable.setBounds(initialBounds); return colors; } @@ -137,6 +144,13 @@ public final class WallpaperColors implements Parcelable { * @param bitmap Source where to extract from. */ public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap) { + return fromBitmap(bitmap, false /* computeHints */); + } + + /** + * @hide + */ + public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap, boolean computeHints) { if (bitmap == null) { throw new IllegalArgumentException("Bitmap can't be null"); } @@ -186,7 +200,7 @@ public final class WallpaperColors implements Parcelable { } } - int hints = calculateDarkHints(bitmap); + int hints = computeHints ? calculateDarkHints(bitmap) : 0; if (shouldRecycle) { bitmap.recycle(); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 16e36bc54ef20eb542a924acded6dc255d60900e..d42fa996ceff49d8350dfc7b0937daf429883cfa 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -56,8 +56,11 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManager.UserOperationException; +import android.os.UserManager.UserOperationResult; import android.provider.ContactsContract.Directory; import android.provider.Settings; import android.security.AttestedKeyPair; @@ -3709,7 +3712,9 @@ public class DevicePolicyManager { public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; /** - * Disable all keyguard widgets. Has no effect. + * Disable all keyguard widgets. Has no effect starting from + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} since keyguard widget is only supported + * on Android versions lower than 5.0. */ public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0; @@ -3729,8 +3734,10 @@ public class DevicePolicyManager { public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 1 << 3; /** - * Ignore trust agent state on secure keyguard screens - * (e.g. PIN/Pattern/Password). + * Disable trust agents on secure keyguard screens (e.g. PIN/Pattern/Password). + * By setting this flag alone, all trust agents are disabled. If the admin then wants to + * whitelist specific features of some trust agent, {@link #setTrustAgentConfiguration} can be + * used in conjuction to set trust-agent-specific configurations. */ public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4; @@ -5574,10 +5581,13 @@ public class DevicePolicyManager { } /** - * Called by a profile owner or device owner to add a default intent handler activity for - * intents that match a certain intent filter. This activity will remain the default intent - * handler even if the set of potential event handlers for the intent filter changes and if the - * intent preferences are reset. + * Called by a profile owner or device owner to set a default activity that the system selects + * to handle intents that match the given {@link IntentFilter}. This activity will remain the + * default intent handler even if the set of potential event handlers for the intent filter + * changes and if the intent preferences are reset. + *

+ * Note that the caller should still declare the activity in the manifest, the API just sets + * the activity to be the default one to handle the given intent filter. *

* The default disambiguation mechanism takes over if the activity is not installed (anymore). * When the activity is (re)installed, it is automatically reset as default intent handler for @@ -5626,6 +5636,29 @@ public class DevicePolicyManager { } } + /** + * Called by a device owner to set the default SMS application. + *

+ * The calling device admin must be a device owner. If it is not, a security exception will be + * thrown. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName The name of the package to set as the default SMS application. + * @throws SecurityException if {@code admin} is not a device owner. + * + * @hide + */ + public void setDefaultSmsApplication(@NonNull ComponentName admin, String packageName) { + throwIfParentInstance("setDefaultSmsApplication"); + if (mService != null) { + try { + mService.setDefaultSmsApplication(admin, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + /** * Called by a profile owner or device owner to grant permission to a package to manage * application restrictions for the calling user via {@link #setApplicationRestrictions} and @@ -5765,11 +5798,20 @@ public class DevicePolicyManager { } /** - * Sets a list of configuration features to enable for a TrustAgent component. This is meant to + * Sets a list of configuration features to enable for a trust agent component. This is meant to * be used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which disables all trust * agents but those enabled by this function call. If flag * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is not set, then this call has no effect. *

+ * For any specific trust agent, whether it is disabled or not depends on the aggregated state + * of each admin's {@link #KEYGUARD_DISABLE_TRUST_AGENTS} setting and its trust agent + * configuration as set by this function call. In particular: if any admin sets + * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} and does not additionally set any + * trust agent configuration, the trust agent is disabled completely. Otherwise, the trust agent + * will receive the list of configurations from all admins who set + * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} and aggregate the configurations to determine its + * behavior. The exact meaning of aggregation is trust-agent-specific. + *

* The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call this method; * if not, a security exception will be thrown. @@ -5779,17 +5821,10 @@ public class DevicePolicyManager { * the parent profile. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param target Component name of the agent to be enabled. - * @param configuration TrustAgent-specific feature bundle. If null for any admin, agent will be - * strictly disabled according to the state of the - * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} flag. - *

- * If {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is set and options is not null for all - * admins, then it's up to the TrustAgent itself to aggregate the values from all - * device admins. - *

- * Consult documentation for the specific TrustAgent to determine legal options - * parameters. + * @param target Component name of the agent to be configured. + * @param configuration Trust-agent-specific feature configuration bundle. Please consult + * documentation of the specific trust agent to determine the interpretation of this + * bundle. * @throws SecurityException if {@code admin} is not an active administrator or does not use * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} */ @@ -5850,7 +5885,7 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param disabled If true caller-Id information in the managed profile is not displayed. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) { throwIfParentInstance("setCrossProfileCallerIdDisabled"); @@ -5871,7 +5906,7 @@ public class DevicePolicyManager { * thrown. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) { throwIfParentInstance("getCrossProfileCallerIdDisabled"); @@ -5911,7 +5946,7 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param disabled If true contacts search in the managed profile is not displayed. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin, boolean disabled) { @@ -5933,7 +5968,7 @@ public class DevicePolicyManager { * thrown. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) { throwIfParentInstance("getCrossProfileContactsSearchDisabled"); @@ -6004,7 +6039,7 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param disabled If true, bluetooth devices cannot access enterprise contacts. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public void setBluetoothContactSharingDisabled(@NonNull ComponentName admin, boolean disabled) { throwIfParentInstance("setBluetoothContactSharingDisabled"); @@ -6027,7 +6062,7 @@ public class DevicePolicyManager { * This API works on managed profile only. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public boolean getBluetoothContactSharingDisabled(@NonNull ComponentName admin) { throwIfParentInstance("getBluetoothContactSharingDisabled"); @@ -6097,7 +6132,7 @@ public class DevicePolicyManager { * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @throws SecurityException if {@code admin} is not a device or profile owner. + * @throws SecurityException if {@code admin} is not a profile owner. */ public void clearCrossProfileIntentFilters(@NonNull ComponentName admin) { throwIfParentInstance("clearCrossProfileIntentFilters"); @@ -6111,21 +6146,22 @@ public class DevicePolicyManager { } /** - * Called by a profile or device owner to set the permitted accessibility services. When set by + * Called by a profile or device owner to set the permitted + * {@link android.accessibilityservice.AccessibilityService}. When set by * a device owner or profile owner the restriction applies to all profiles of the user the - * device owner or profile owner is an admin for. By default the user can use any accessiblity - * service. When zero or more packages have been added, accessiblity services that are not in + * device owner or profile owner is an admin for. By default, the user can use any accessibility + * service. When zero or more packages have been added, accessibility services that are not in * the list and not part of the system can not be enabled by the user. *

* Calling with a null value for the list disables the restriction so that all services can be - * used, calling with an empty list only allows the builtin system's services. + * used, calling with an empty list only allows the built-in system services. Any non-system + * accessibility service that's currently enabled must be included in the list. *

* System accessibility services are always available to the user the list can't modify this. - * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageNames List of accessibility service package names. - * @return true if setting the restriction succeeded. It fail if there is one or more non-system - * accessibility services enabled, that are not in the list. + * @return {@code true} if the operation succeeded, or {@code false} if the list didn't + * contain every enabled non-system accessibility service. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin, @@ -6214,10 +6250,11 @@ public class DevicePolicyManager { /** * Called by a profile or device owner to set the permitted input methods services. When set by * a device owner or profile owner the restriction applies to all profiles of the user the - * device owner or profile owner is an admin for. By default the user can use any input method. + * device owner or profile owner is an admin for. By default, the user can use any input method. * When zero or more packages have been added, input method that are not in the list and not * part of the system can not be enabled by the user. This method will fail if it is called for - * a admin that is not for the foreground user or a profile of the foreground user. + * a admin that is not for the foreground user or a profile of the foreground user. Any + * non-system input method service that's currently enabled must be included in the list. *

* Calling with a null value for the list disables the restriction so that all input methods can * be used, calling with an empty list disables all but the system's own input methods. @@ -6226,8 +6263,8 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageNames List of input method package names. - * @return true if setting the restriction succeeded. It will fail if there are one or more - * non-system input methods currently enabled that are not in the packageNames list. + * @return {@code true} if the operation succeeded, or {@code false} if the list didn't + * contain every enabled non-system input method service. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public boolean setPermittedInputMethods( @@ -6549,6 +6586,9 @@ public class DevicePolicyManager { *

* If the adminExtras are not null, they will be stored on the device until the user is started * for the first time. Then the extras will be passed to the admin when onEnable is called. + *

From {@link android.os.Build.VERSION_CODES#P} onwards, if targeting + * {@link android.os.Build.VERSION_CODES#P}, throws {@link UserOperationException} instead of + * returning {@code null} on failure. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param name The user's name. @@ -6563,6 +6603,9 @@ public class DevicePolicyManager { * @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the * user could not be created. * @throws SecurityException if {@code admin} is not a device owner. + * @throws UserOperationException if the user could not be created and the calling app is + * targeting {@link android.os.Build.VERSION_CODES#P} and running on + * {@link android.os.Build.VERSION_CODES#P}. */ public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name, @@ -6571,6 +6614,8 @@ public class DevicePolicyManager { throwIfParentInstance("createAndManageUser"); try { return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags); + } catch (ServiceSpecificException e) { + throw new UserOperationException(e.getMessage(), e.errorCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -6613,78 +6658,16 @@ public class DevicePolicyManager { } } - /** - * Indicates user operation is successful. - * - * @see #startUserInBackground(ComponentName, UserHandle) - * @see #stopUser(ComponentName, UserHandle) - * @see #logoutUser(ComponentName) - */ - public static final int USER_OPERATION_SUCCESS = 0; - - /** - * Indicates user operation failed for unknown reason. - * - * @see #startUserInBackground(ComponentName, UserHandle) - * @see #stopUser(ComponentName, UserHandle) - * @see #logoutUser(ComponentName) - */ - public static final int USER_OPERATION_ERROR_UNKNOWN = 1; - - /** - * Indicates user operation failed because target user is a managed profile. - * - * @see #startUserInBackground(ComponentName, UserHandle) - * @see #stopUser(ComponentName, UserHandle) - * @see #logoutUser(ComponentName) - */ - public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; - - /** - * Indicates user operation failed because maximum running user limit has reached. - * - * @see #startUserInBackground(ComponentName, UserHandle) - */ - public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; - - /** - * Indicates user operation failed because the target user is in foreground. - * - * @see #stopUser(ComponentName, UserHandle) - * @see #logoutUser(ComponentName) - */ - public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; - - /** - * Result returned from - *

    - *
  • {@link #startUserInBackground(ComponentName, UserHandle)}
  • - *
  • {@link #stopUser(ComponentName, UserHandle)}
  • - *
  • {@link #logoutUser(ComponentName)}
  • - *
- * - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "USER_OPERATION_" }, value = { - USER_OPERATION_SUCCESS, - USER_OPERATION_ERROR_UNKNOWN, - USER_OPERATION_ERROR_MANAGED_PROFILE, - USER_OPERATION_ERROR_MAX_RUNNING_USERS, - USER_OPERATION_ERROR_CURRENT_USER - }) - public @interface UserOperationResult {} - /** * Called by a device owner to start the specified secondary user in background. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param userHandle the user to be started in background. * @return one of the following result codes: - * {@link #USER_OPERATION_ERROR_UNKNOWN}, - * {@link #USER_OPERATION_SUCCESS}, - * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE}, - * {@link #USER_OPERATION_ERROR_MAX_RUNNING_USERS}, + * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN}, + * {@link UserManager#USER_OPERATION_SUCCESS}, + * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE}, + * {@link UserManager#USER_OPERATION_ERROR_MAX_RUNNING_USERS}, * @throws SecurityException if {@code admin} is not a device owner. * @see #getSecondaryUsers(ComponentName) */ @@ -6704,10 +6687,10 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param userHandle the user to be stopped. * @return one of the following result codes: - * {@link #USER_OPERATION_ERROR_UNKNOWN}, - * {@link #USER_OPERATION_SUCCESS}, - * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE}, - * {@link #USER_OPERATION_ERROR_CURRENT_USER} + * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN}, + * {@link UserManager#USER_OPERATION_SUCCESS}, + * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE}, + * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER} * @throws SecurityException if {@code admin} is not a device owner. * @see #getSecondaryUsers(ComponentName) */ @@ -6727,10 +6710,10 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @return one of the following result codes: - * {@link #USER_OPERATION_ERROR_UNKNOWN}, - * {@link #USER_OPERATION_SUCCESS}, - * {@link #USER_OPERATION_ERROR_MANAGED_PROFILE}, - * {@link #USER_OPERATION_ERROR_CURRENT_USER} + * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN}, + * {@link UserManager#USER_OPERATION_SUCCESS}, + * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE}, + * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER} * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device. * @see #getSecondaryUsers(ComponentName) */ @@ -7880,11 +7863,14 @@ public class DevicePolicyManager { * {@link #PERMISSION_GRANT_STATE_DEFAULT default} in which a user can manage it through the UI, * {@link #PERMISSION_GRANT_STATE_DENIED denied}, in which the permission is denied and the user * cannot manage it through the UI, and {@link #PERMISSION_GRANT_STATE_GRANTED granted} in which - * the permission is granted and the user cannot manage it through the UI. This might affect all - * permissions in a group that the runtime permission belongs to. This method can only be called - * by a profile owner, device owner, or a delegate given the + * the permission is granted and the user cannot manage it through the UI. This method can only + * be called by a profile owner, device owner, or a delegate given the * {@link #DELEGATION_PERMISSION_GRANT} scope via {@link #setDelegatedScopes}. *

+ * Note that user cannot manage other permissions in the affected group through the UI + * either and their granted state will be kept as the current value. Thus, it's recommended that + * you set the grant state of all the permissions in the affected group. + *

* Setting the grant state to {@link #PERMISSION_GRANT_STATE_DEFAULT default} does not revoke * the permission. It retains the previous grant, if any. *

@@ -9420,6 +9406,11 @@ public class DevicePolicyManager { /** * Called by device owner to add an override APN. * + *

This method may returns {@code -1} if {@code apnSetting} conflicts with an existing + * override APN. Update the existing conflicted APN with + * {@link #updateOverrideApn(ComponentName, int, ApnSetting)} instead of adding a new entry. + *

See {@link ApnSetting} for the definition of conflict. + * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param apnSetting the override APN to insert * @return The {@code id} of inserted override APN. Or {@code -1} when failed to insert into @@ -9443,6 +9434,12 @@ public class DevicePolicyManager { /** * Called by device owner to update an override APN. * + *

This method may returns {@code false} if there is no override APN with the given + * {@code apnId}. + *

This method may also returns {@code false} if {@code apnSetting} conflicts with an + * existing override APN. Update the existing conflicted APN instead. + *

See {@link ApnSetting} for the definition of conflict. + * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param apnId the {@code id} of the override APN to update * @param apnSetting the override APN to update @@ -9468,6 +9465,9 @@ public class DevicePolicyManager { /** * Called by device owner to remove an override APN. * + *

This method may returns {@code false} if there is no override APN with the given + * {@code apnId}. + * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param apnId the {@code id} of the override APN to remove * @return {@code true} if the required override APN is successfully removed, {@code false} diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 5218a7340ec93ff0e9e1eab015ff95065230bac8..c29369fe96a833721e853324233a7535d4e63fe2 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -192,6 +192,8 @@ interface IDevicePolicyManager { void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity); void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName); + void setDefaultSmsApplication(in ComponentName admin, String packageName); + void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings); Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName); boolean setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 1312a2e6b623a52ab7afad5ade0f32a4dfc63701..d568662409d7a37d47b396ca118f7357d73d1daf 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1309,6 +1309,11 @@ public class AssistStructure implements Parcelable { if (domain == null) return; final Uri uri = Uri.parse(domain); + if (uri == null) { + // Cannot log domain because it could contain PII; + Log.w(TAG, "Failed to parse web domain"); + return; + } mWebScheme = uri.getScheme(); mWebDomain = uri.getHost(); } @@ -2232,6 +2237,22 @@ public class AssistStructure implements Parcelable { return mWindowNodes.get(index); } + // TODO(b/35708678): temporary method that disable one-way warning flag on binder. + /** @hide */ + public void ensureDataForAutofill() { + if (mHaveData) { + return; + } + mHaveData = true; + Binder.allowBlocking(mReceiveChannel); + try { + ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel); + reader.go(); + } finally { + Binder.defaultBlocking(mReceiveChannel); + } + } + /** @hide */ public void ensureData() { if (mHaveData) { diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index f456395aa2b53fc7cc3ffdb3d07b20a6ad0f3005..0963594bc00e0ae817b23900656a94f2d9e807b9 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -83,6 +83,13 @@ public class BackupTransport { */ public static final int FLAG_NON_INCREMENTAL = 1 << 2; + /** + * Used as a boolean extra in the binding intent of transports. We pass {@code true} to + * notify transports that the current connection is used for registering the transport. + */ + public static final String EXTRA_TRANSPORT_REGISTRATION = + "android.app.backup.extra.TRANSPORT_REGISTRATION"; + IBackupTransport mBinderImpl = new TransportImpl(); public IBinder getBinder() { diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java index 73b5ec44044162eda69377073009b2e8b16ba20a..545463c124bbae33372341c89183632feca6d4fc 100644 --- a/core/java/android/app/servertransaction/ActivityResultItem.java +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -16,7 +16,7 @@ package android.app.servertransaction; -import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.app.ClientTransactionHandler; @@ -38,8 +38,8 @@ public class ActivityResultItem extends ClientTransactionItem { private List mResultInfoList; @Override - public int getPreExecutionState() { - return ON_PAUSE; + public int getPostExecutionState() { + return ON_RESUME; } @Override diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java index 6f2cc007ac2763014cc959b9fb9a98a0a8cd2414..d94f08b6aac1c2419da3a93845538bed6005a42c 100644 --- a/core/java/android/app/servertransaction/ClientTransactionItem.java +++ b/core/java/android/app/servertransaction/ClientTransactionItem.java @@ -32,12 +32,6 @@ import android.os.Parcelable; */ public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable { - /** Get the state in which this callback can be executed. */ - @LifecycleState - public int getPreExecutionState() { - return UNDEFINED; - } - /** Get the state that must follow this callback. */ @LifecycleState public int getPostExecutionState() { diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java index 7dfde73c0534cbc5f86caab94e91e95d6f31f660..e5ce3b00c0fd7e5b8188ac5c136b8d77643f2978 100644 --- a/core/java/android/app/servertransaction/NewIntentItem.java +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -38,11 +38,6 @@ public class NewIntentItem extends ClientTransactionItem { // TODO(lifecycler): Switch new intent handling to this scheme. /*@Override - public int getPreExecutionState() { - return ON_PAUSE; - } - - @Override public int getPostExecutionState() { return ON_RESUME; }*/ diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 578f0e3d24cb657080c8d458ba49826aaa928a6d..65e429126ac22f7f67df279bff0e3a44ae2d5f1a 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -42,8 +42,8 @@ public class PauseActivityItem extends ActivityLifecycleItem { public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); - client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport, - pendingActions, "PAUSE_ACTIVITY_ITEM"); + client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions, + "PAUSE_ACTIVITY_ITEM"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index b66d61b76d5fa7386d8082670c25cf1c28ce8e74..0e52b34715046008a967744f2933fb131a684a02 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -24,6 +24,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; import static android.app.servertransaction.ActivityLifecycleItem.ON_START; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; +import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; @@ -48,11 +49,7 @@ public class TransactionExecutor { private ClientTransactionHandler mTransactionHandler; private PendingTransactionActions mPendingActions = new PendingTransactionActions(); - - // Temp holder for lifecycle path. - // No direct transition between two states should take more than one complete cycle of 6 states. - @ActivityLifecycleItem.LifecycleState - private IntArray mLifecycleSequence = new IntArray(6); + private TransactionExecutorHelper mHelper = new TransactionExecutorHelper(); /** Initialize an instance with transaction handler, that will execute all requested actions. */ public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) { @@ -89,13 +86,25 @@ public class TransactionExecutor { final IBinder token = transaction.getActivityToken(); ActivityClientRecord r = mTransactionHandler.getActivityClient(token); + + // In case when post-execution state of the last callback matches the final state requested + // for the activity in this transaction, we won't do the last transition here and do it when + // moving to final state instead (because it may contain additional parameters from server). + final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest(); + final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState() + : UNDEFINED; + // Index of the last callback that requests some post-execution state. + final int lastCallbackRequestingState = lastCallbackRequestingState(transaction); + final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); log("Resolving callback: " + item); - final int preExecutionState = item.getPreExecutionState(); - if (preExecutionState != UNDEFINED) { - cycleToPath(r, preExecutionState); + final int postExecutionState = item.getPostExecutionState(); + final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, + item.getPostExecutionState()); + if (closestPreExecutionState != UNDEFINED) { + cycleToPath(r, closestPreExecutionState); } item.execute(mTransactionHandler, token, mPendingActions); @@ -105,9 +114,11 @@ public class TransactionExecutor { r = mTransactionHandler.getActivityClient(token); } - final int postExecutionState = item.getPostExecutionState(); - if (postExecutionState != UNDEFINED) { - cycleToPath(r, postExecutionState); + if (postExecutionState != UNDEFINED && r != null) { + // Skip the very last transition and perform it by explicit state request instead. + final boolean shouldExcludeLastTransition = + i == lastCallbackRequestingState && finalState == postExecutionState; + cycleToPath(r, postExecutionState, shouldExcludeLastTransition); } } } @@ -162,15 +173,15 @@ public class TransactionExecutor { boolean excludeLastState) { final int start = r.getLifecycleState(); log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState); - initLifecyclePath(start, finish, excludeLastState); - performLifecycleSequence(r); + final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState); + performLifecycleSequence(r, path); } /** Transition the client through previously initialized state sequence. */ - private void performLifecycleSequence(ActivityClientRecord r) { - final int size = mLifecycleSequence.size(); + private void performLifecycleSequence(ActivityClientRecord r, IntArray path) { + final int size = path.size(); for (int i = 0, state; i < size; i++) { - state = mLifecycleSequence.get(i); + state = path.get(i); log("Transitioning to state: " + state); switch (state) { case ON_CREATE: @@ -185,8 +196,8 @@ public class TransactionExecutor { break; case ON_PAUSE: mTransactionHandler.handlePauseActivity(r.token, false /* finished */, - false /* userLeaving */, 0 /* configChanges */, - true /* dontReport */, mPendingActions, "LIFECYCLER_PAUSE_ACTIVITY"); + false /* userLeaving */, 0 /* configChanges */, mPendingActions, + "LIFECYCLER_PAUSE_ACTIVITY"); break; case ON_STOP: mTransactionHandler.handleStopActivity(r.token, false /* show */, @@ -195,8 +206,7 @@ public class TransactionExecutor { case ON_DESTROY: mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */, 0 /* configChanges */, false /* getNonConfigInstance */, - "performLifecycleSequence. cycling to:" - + mLifecycleSequence.get(size - 1)); + "performLifecycleSequence. cycling to:" + path.get(size - 1)); break; case ON_RESTART: mTransactionHandler.performRestartActivity(r.token, false /* start */); @@ -207,60 +217,6 @@ public class TransactionExecutor { } } - /** - * Calculate the path through main lifecycle states for an activity and fill - * @link #mLifecycleSequence} with values starting with the state that follows the initial - * state. - */ - public void initLifecyclePath(int start, int finish, boolean excludeLastState) { - mLifecycleSequence.clear(); - if (finish >= start) { - // just go there - for (int i = start + 1; i <= finish; i++) { - mLifecycleSequence.add(i); - } - } else { // finish < start, can't just cycle down - if (start == ON_PAUSE && finish == ON_RESUME) { - // Special case when we can just directly go to resumed state. - mLifecycleSequence.add(ON_RESUME); - } else if (start <= ON_STOP && finish >= ON_START) { - // Restart and go to required state. - - // Go to stopped state first. - for (int i = start + 1; i <= ON_STOP; i++) { - mLifecycleSequence.add(i); - } - // Restart - mLifecycleSequence.add(ON_RESTART); - // Go to required state - for (int i = ON_START; i <= finish; i++) { - mLifecycleSequence.add(i); - } - } else { - // Relaunch and go to required state - - // Go to destroyed state first. - for (int i = start + 1; i <= ON_DESTROY; i++) { - mLifecycleSequence.add(i); - } - // Go to required state - for (int i = ON_CREATE; i <= finish; i++) { - mLifecycleSequence.add(i); - } - } - } - - // Remove last transition in case we want to perform it with some specific params. - if (excludeLastState && mLifecycleSequence.size() != 0) { - mLifecycleSequence.remove(mLifecycleSequence.size() - 1); - } - } - - @VisibleForTesting - public int[] getLifecycleSequence() { - return mLifecycleSequence.toArray(); - } - private static void log(String message) { if (DEBUG_RESOLVER) Slog.d(TAG, message); } diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..7e66fd7a2ead9abdeb641dcbea33c94a62a7ceaa --- /dev/null +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -0,0 +1,225 @@ +/* + * Copyright 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.servertransaction; + +import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; +import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; +import static android.app.servertransaction.ActivityLifecycleItem.ON_START; +import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; +import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; +import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; + +import android.app.ActivityThread; +import android.util.IntArray; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; + +/** + * Helper class for {@link TransactionExecutor} that contains utils for lifecycle path resolution. + * @hide + */ +public class TransactionExecutorHelper { + // A penalty applied to path with destruction when looking for the shortest one. + private static final int DESTRUCTION_PENALTY = 10; + + private static final int[] ON_RESUME_PRE_EXCUTION_STATES = new int[] { ON_START, ON_PAUSE }; + + // Temp holder for lifecycle path. + // No direct transition between two states should take more than one complete cycle of 6 states. + @ActivityLifecycleItem.LifecycleState + private IntArray mLifecycleSequence = new IntArray(6); + + /** + * Calculate the path through main lifecycle states for an activity and fill + * @link #mLifecycleSequence} with values starting with the state that follows the initial + * state. + *

NOTE: The returned value is used internally in this class and is not a copy. It's contents + * may change after calling other methods of this class.

+ */ + @VisibleForTesting + public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) { + if (start == UNDEFINED || finish == UNDEFINED) { + throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state"); + } + if (start == ON_RESTART || finish == ON_RESTART) { + throw new IllegalArgumentException( + "Can't start or finish in intermittent RESTART state"); + } + if (finish == PRE_ON_CREATE && start != finish) { + throw new IllegalArgumentException("Can only start in pre-onCreate state"); + } + + mLifecycleSequence.clear(); + if (finish >= start) { + // just go there + for (int i = start + 1; i <= finish; i++) { + mLifecycleSequence.add(i); + } + } else { // finish < start, can't just cycle down + if (start == ON_PAUSE && finish == ON_RESUME) { + // Special case when we can just directly go to resumed state. + mLifecycleSequence.add(ON_RESUME); + } else if (start <= ON_STOP && finish >= ON_START) { + // Restart and go to required state. + + // Go to stopped state first. + for (int i = start + 1; i <= ON_STOP; i++) { + mLifecycleSequence.add(i); + } + // Restart + mLifecycleSequence.add(ON_RESTART); + // Go to required state + for (int i = ON_START; i <= finish; i++) { + mLifecycleSequence.add(i); + } + } else { + // Relaunch and go to required state + + // Go to destroyed state first. + for (int i = start + 1; i <= ON_DESTROY; i++) { + mLifecycleSequence.add(i); + } + // Go to required state + for (int i = ON_CREATE; i <= finish; i++) { + mLifecycleSequence.add(i); + } + } + } + + // Remove last transition in case we want to perform it with some specific params. + if (excludeLastState && mLifecycleSequence.size() != 0) { + mLifecycleSequence.remove(mLifecycleSequence.size() - 1); + } + + return mLifecycleSequence; + } + + /** + * Pick a state that goes before provided post-execution state and would require the least + * lifecycle transitions to get to. + * It will also make sure to try avoiding a path with activity destruction and relaunch if + * possible. + * @param r An activity that we're trying to resolve the transition for. + * @param postExecutionState Post execution state to compute for. + * @return One of states that precede the provided post-execution state, or + * {@link ActivityLifecycleItem#UNDEFINED} if there is not path. + */ + @VisibleForTesting + public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r, + int postExecutionState) { + switch (postExecutionState) { + case UNDEFINED: + return UNDEFINED; + case ON_RESUME: + return getClosestOfStates(r, ON_RESUME_PRE_EXCUTION_STATES); + default: + throw new UnsupportedOperationException("Pre-execution states for state: " + + postExecutionState + " is not supported."); + } + } + + /** + * Pick a state that would require the least lifecycle transitions to get to. + * It will also make sure to try avoiding a path with activity destruction and relaunch if + * possible. + * @param r An activity that we're trying to resolve the transition for. + * @param finalStates An array of valid final states. + * @return One of the provided final states, or {@link ActivityLifecycleItem#UNDEFINED} if none + * were provided or there is not path. + */ + @VisibleForTesting + public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) { + if (finalStates == null || finalStates.length == 0) { + return UNDEFINED; + } + + final int currentState = r.getLifecycleState(); + int closestState = UNDEFINED; + for (int i = 0, shortestPath = Integer.MAX_VALUE, pathLength; i < finalStates.length; i++) { + getLifecyclePath(currentState, finalStates[i], false /* excludeLastState */); + pathLength = mLifecycleSequence.size(); + if (pathInvolvesDestruction(mLifecycleSequence)) { + pathLength += DESTRUCTION_PENALTY; + } + if (shortestPath > pathLength) { + shortestPath = pathLength; + closestState = finalStates[i]; + } + } + return closestState; + } + + /** + * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence + * that involves destruction and recreation if there is another path. + */ + private static boolean pathInvolvesDestruction(IntArray lifecycleSequence) { + final int size = lifecycleSequence.size(); + for (int i = 0; i < size; i++) { + if (lifecycleSequence.get(i) == ON_DESTROY) { + return true; + } + } + return false; + } + + /** + * Return the index of the last callback that requests the state in which activity will be after + * execution. If there is a group of callbacks in the end that requests the same specific state + * or doesn't request any - we will find the first one from such group. + * + * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any + * specific state. If there is a sequence + * Configuration - ActivityResult - Configuration - ActivityResult + * index 1 will be returned, because ActivityResult request on position 1 will be the last + * request that moves activity to the RESUMED state where it will eventually end. + */ + static int lastCallbackRequestingState(ClientTransaction transaction) { + final List callbacks = transaction.getCallbacks(); + if (callbacks == null || callbacks.size() == 0) { + return -1; + } + + // Go from the back of the list to front, look for the request closes to the beginning that + // requests the state in which activity will end after all callbacks are executed. + int lastRequestedState = UNDEFINED; + int lastRequestingCallback = -1; + for (int i = callbacks.size() - 1; i >= 0; i--) { + final ClientTransactionItem callback = callbacks.get(i); + final int postExecutionState = callback.getPostExecutionState(); + if (postExecutionState != UNDEFINED) { + // Found a callback that requests some post-execution state. + if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) { + // It's either a first-from-end callback that requests state or it requests + // the same state as the last one. In both cases, we will use it as the new + // candidate. + lastRequestedState = postExecutionState; + lastRequestingCallback = i; + } else { + break; + } + } + } + + return lastRequestingCallback; + } +} diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index 0a5795e471f5af71b1ad7be71920cb920747d10d..d6f23521bf3c559b3fb50c9666973139ab9b1de4 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -65,7 +65,8 @@ public final class Slice implements Parcelable { HINT_TOGGLE, HINT_HORIZONTAL, HINT_PARTIAL, - HINT_SEE_MORE + HINT_SEE_MORE, + HINT_KEY_WORDS }) @Retention(RetentionPolicy.SOURCE) public @interface SliceHint {} @@ -141,13 +142,15 @@ public final class Slice implements Parcelable { */ public static final String HINT_SEE_MORE = "see_more"; /** - * A hint used when implementing app-specific slice permissions. - * Tells the system that for this slice the return value of - * {@link SliceProvider#onBindSlice(Uri, List)} may be different depending on - * {@link SliceProvider#getBindingPackage} and should not be cached for multiple - * apps. + * @see Builder#setCallerNeeded + * @hide */ public static final String HINT_CALLER_NEEDED = "caller_needed"; + /** + * A hint to indicate that the contents of this subslice represent a list of keywords + * related to the parent slice. + */ + public static final String HINT_KEY_WORDS = "key_words"; /** * Key to retrieve an extra added to an intent when a control is changed. */ @@ -283,6 +286,14 @@ public final class Slice implements Parcelable { return ArrayUtils.contains(mHints, hint); } + /** + * Returns whether the caller for this slice matters. + * @see Builder#setCallerNeeded + */ + public boolean isCallerNeeded() { + return hasHint(HINT_CALLER_NEEDED); + } + /** * A Builder used to construct {@link Slice}s */ @@ -311,6 +322,21 @@ public final class Slice implements Parcelable { .appendPath(String.valueOf(mItems.size())).build(); } + /** + * Tells the system whether for this slice the return value of + * {@link SliceProvider#onBindSlice(Uri, List)} may be different depending on + * {@link SliceProvider#getCallingPackage()} and should not be cached for multiple + * apps. + */ + public Builder setCallerNeeded(boolean callerNeeded) { + if (callerNeeded) { + mHints.add(HINT_CALLER_NEEDED); + } else { + mHints.remove(HINT_CALLER_NEEDED); + } + return this; + } + /** * Add hints to the Slice being constructed */ @@ -436,7 +462,7 @@ public final class Slice implements Parcelable { } /** - * Add a color to the slice being constructed + * Add an integer to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ @@ -446,7 +472,7 @@ public final class Slice implements Parcelable { } /** - * Add a color to the slice being constructed + * Add an integer to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index ae1d8d79caf0ab1e6673191322d60845c5c6db43..4f3cd63841eb3f598452bf1cbd40db075aae7ea4 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -16,9 +16,10 @@ package android.app.slice; -import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; import android.content.ContentProviderClient; import android.content.ContentResolver; @@ -34,9 +35,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import com.android.internal.util.Preconditions; @@ -45,7 +44,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.Executor; /** * Class to handle interactions with {@link Slice}s. @@ -63,6 +61,18 @@ public class SliceManager { public static final String ACTION_REQUEST_SLICE_PERMISSION = "android.intent.action.REQUEST_SLICE_PERMISSION"; + /** + * Category used to resolve intents that can be rendered as slices. + *

+ * This category should be included on intent filters on providers that extend + * {@link SliceProvider}. + * @see SliceProvider + * @see SliceProvider#onMapIntentToUri(Intent) + * @see #mapIntentToUri(Intent) + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_SLICE = "android.app.slice.category.SLICE"; + /** * The meta-data key that allows an activity to easily be linked directly to a slice. *

@@ -74,8 +84,6 @@ public class SliceManager { private final ISliceManager mService; private final Context mContext; - private final ArrayMap, ISliceListener> mListenerLookup = - new ArrayMap<>(); private final IBinder mToken = new Binder(); /** @@ -103,71 +111,6 @@ public class SliceManager { ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE)); } - /** - * @deprecated TO BE REMOVED. - */ - @Deprecated - public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, - @NonNull List specs) { - } - - /** - * @deprecated TO BE REMOVED. - */ - @Deprecated - public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback, - @NonNull List specs, Executor executor) { - } - - /** - * Adds a callback to a specific slice uri. - *

- * This is a convenience that performs a few slice actions at once. It will put - * the slice in a pinned state since there is a callback attached. It will also - * listen for content changes, when a content change observes, the android system - * will bind the new slice and provide it to all registered {@link SliceCallback}s. - * - * @param uri The uri of the slice being listened to. - * @param callback The listener that should receive the callbacks. - * @param specs The list of supported {@link SliceSpec}s of the callback. - * @see SliceProvider#onSlicePinned(Uri) - */ - public void registerSliceCallback(@NonNull Uri uri, @NonNull List specs, - @NonNull SliceCallback callback) { - } - - /** - * Adds a callback to a specific slice uri. - *

- * This is a convenience that performs a few slice actions at once. It will put - * the slice in a pinned state since there is a callback attached. It will also - * listen for content changes, when a content change observes, the android system - * will bind the new slice and provide it to all registered {@link SliceCallback}s. - * - * @param uri The uri of the slice being listened to. - * @param callback The listener that should receive the callbacks. - * @param specs The list of supported {@link SliceSpec}s of the callback. - * @see SliceProvider#onSlicePinned(Uri) - */ - public void registerSliceCallback(@NonNull Uri uri, @NonNull List specs, - @NonNull @CallbackExecutor Executor executor, @NonNull SliceCallback callback) { - - } - - /** - * Removes a callback for a specific slice uri. - *

- * Removes the app from the pinned state (if there are no other apps/callbacks pinning it) - * in addition to removing the callback. - * - * @param uri The uri of the slice being listened to - * @param callback The listener that should no longer receive callbacks. - * @see #registerSliceCallback - */ - public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) { - - } - /** * Ensures that a slice is in a pinned state. *

@@ -297,6 +240,18 @@ public class SliceManager { /** * Turns a slice intent into a slice uri. Expects an explicit intent. + *

+ * This goes through a several stage resolution process to determine if any slice + * can represent this intent. + * - If the intent contains data that {@link ContentResolver#getType} is + * {@link SliceProvider#SLICE_TYPE} then the data will be returned. + * - If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then + * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result + * will be returned. + * - Lastly, if the intent explicitly points at an activity, and that activity has + * meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be + * returned. + * - If no slice is found, then {@code null} is returned. * * @param intent The intent associated with a slice. * @return The Slice Uri provided by the app or null if none exists. @@ -316,8 +271,12 @@ public class SliceManager { return intentData; } // Otherwise ask the app + Intent queryIntent = new Intent(intent); + if (!queryIntent.hasCategory(CATEGORY_SLICE)) { + queryIntent.addCategory(CATEGORY_SLICE); + } List providers = - mContext.getPackageManager().queryIntentContentProviders(intent, 0); + mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0); if (providers == null || providers.isEmpty()) { // There are no providers, see if this activity has a direct link. ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent, @@ -451,18 +410,4 @@ public class SliceManager { throw e.rethrowFromSystemServer(); } } - - /** - * Class that listens to changes in {@link Slice}s. - */ - public interface SliceCallback { - - /** - * Called when slice is updated. - * - * @param s The updated slice. - * @see #registerSliceCallback - */ - void onSliceUpdated(Slice s); - } } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index af4303263da55a49c54f0706079a08c17ed1ee59..df32fb9c2e75fd990b92e0bf7148e6a09c2965e8 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -65,8 +65,8 @@ import java.util.concurrent.CountDownLatch; *

  * {@literal
  * }
+ *     android:name="com.example.mypkg.MySliceProvider"
+ *     android:authorities="com.example.mypkg" />}
  * 
*

* Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider @@ -77,10 +77,11 @@ import java.util.concurrent.CountDownLatch; *

  * {@literal
  * 
+ *     android:name="com.example.mypkg.MySliceProvider"
+ *     android:authorities="com.example.mypkg">
  *     
- *         
+ *         
+ *         
  *     
  * }
  * 
@@ -253,8 +254,13 @@ public abstract class SliceProvider extends ContentProvider { * In that case, this method can be called and is expected to return a non-null Uri representing * a slice. Otherwise this will throw {@link UnsupportedOperationException}. * + * Any intent filter added to a slice provider should also contain + * {@link SliceManager#CATEGORY_SLICE}, because otherwise it will not be detected by + * {@link SliceManager#mapIntentToUri(Intent)}. + * * @return Uri representing the slice associated with the provided intent. - * @see {@link Slice} + * @see Slice + * @see SliceManager#mapIntentToUri(Intent) */ public @NonNull Uri onMapIntentToUri(Intent intent) { throw new UnsupportedOperationException( diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index e72b84d248024c177a63b3c075a3cc6821efdf52..fff1a00c585ef9365c3ce89c4807e57391aef2b8 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -32,6 +32,7 @@ interface IUsageStatsManager { ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime, String callingPackage); UsageEvents queryEvents(long beginTime, long endTime, String callingPackage); + UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage); void setAppInactive(String packageName, boolean inactive, int userId); boolean isAppInactive(String packageName, int userId); void whitelistAppTemporarily(String packageName, long duration, int userId); diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 5a57b067f2c435f7c40d36feff774319331a9afa..5f9fa43203dad9718fe2606539520c9848e42795 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -52,10 +52,13 @@ import java.util.Map; * * A request for data in the middle of a time interval will include that interval. *

- * NOTE: This API requires the permission android.permission.PACKAGE_USAGE_STATS. - * However, declaring the permission implies intention to use the API and the user of the device - * still needs to grant permission through the Settings application. - * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS} + * NOTE: Most methods on this API require the permission + * android.permission.PACKAGE_USAGE_STATS. However, declaring the permission implies intention to + * use the API and the user of the device still needs to grant permission through the Settings + * application. + * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS}. + * Methods which only return the information for the calling package do not require this permission. + * E.g. {@link #getAppStandbyBucket()} and {@link #queryEventsForSelf(long, long)}. */ @SystemService(Context.USAGE_STATS_SERVICE) public final class UsageStatsManager { @@ -206,6 +209,8 @@ public final class UsageStatsManager { * 2014 - com.example.charlie * * + *

The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS}

+ * * @param intervalType The time interval by which the stats are aggregated. * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. @@ -235,6 +240,7 @@ public final class UsageStatsManager { * Gets the hardware configurations the device was in for the given time range, aggregated by * the specified interval. The results are ordered as in * {@link #queryUsageStats(int, long, long)}. + *

The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS}

* * @param intervalType The time interval by which the stats are aggregated. * @param beginTime The inclusive beginning of the range of stats to include in the results. @@ -259,6 +265,7 @@ public final class UsageStatsManager { /** * Query for events in the given time range. Events are only kept by the system for a few * days. + *

The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS}

* * @param beginTime The inclusive beginning of the range of events to include in the results. * @param endTime The exclusive end of the range of events to include in the results. @@ -277,10 +284,33 @@ public final class UsageStatsManager { return sEmptyResults; } + /** + * Like {@link #queryEvents(long, long)}, but only returns events for the calling package. + * + * @param beginTime The inclusive beginning of the range of events to include in the results. + * @param endTime The exclusive end of the range of events to include in the results. + * @return A {@link UsageEvents} object. + * + * @see #queryEvents(long, long) + */ + public UsageEvents queryEventsForSelf(long beginTime, long endTime) { + try { + final UsageEvents events = mService.queryEventsForPackage(beginTime, endTime, + mContext.getOpPackageName()); + if (events != null) { + return events; + } + } catch (RemoteException e) { + // fallthrough + } + return sEmptyResults; + } + /** * A convenience method that queries for all stats in the given range (using the best interval * for that range), merges the resulting data, and keys it by package name. * See {@link #queryUsageStats(int, long, long)}. + *

The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS}

* * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index a2e714eb348a9dc519e6ada46c966e43d3f9ae72..94fd138ca6e84ca19d43103b402e0c85f858cbec 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -25,7 +25,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.media.AudioManager; import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; @@ -598,34 +597,6 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - /** - * Tells remote device to adjust volume. Only if absolute volume is - * supported. Uses the following values: - *
    - *
  • {@link AudioManager#ADJUST_LOWER}
  • - *
  • {@link AudioManager#ADJUST_RAISE}
  • - *
  • {@link AudioManager#ADJUST_MUTE}
  • - *
  • {@link AudioManager#ADJUST_UNMUTE}
  • - *
- * - * @param direction One of the supported adjust values. - * @hide - */ - public void adjustAvrcpAbsoluteVolume(int direction) { - if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume"); - try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.adjustAvrcpAbsoluteVolume(direction); - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); - } finally { - mServiceLock.readLock().unlock(); - } - } - /** * Tells remote device to set an absolute volume. Only if absolute volume is supported * diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1dc7549e763a9de1dd6e966f37a04b7efba863fb..ee667c2207795a3db54b481039057fa00d97da0c 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -541,13 +541,14 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; /** The profile is in disconnecting state */ - public static final int STATE_DISCONNECTING = 3; + public static final int STATE_DISCONNECTING = + BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 1a5de5690caeaea3355ac8d0a789c4dc2ef4b4de..cb353576bd8d5c858be07604d0eed8d69e6f6b4f 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -84,7 +84,7 @@ public final class CompanionDeviceManager { public abstract void onDeviceFound(IntentSender chooserLauncher); /** - * Called if there was an error looking for device(s), e.g. timeout + * Called if there was an error looking for device(s) * * @param error the cause of the error */ diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 718e465bf0de253dfda179437e867455b26b94fb..73b6eb27bed36ff8163f7d8922105997fb8a369a 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -16,13 +16,16 @@ package android.content; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemService; import android.os.Handler; -import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import com.android.internal.util.Preconditions; + import java.util.ArrayList; /** @@ -45,6 +48,7 @@ import java.util.ArrayList; @SystemService(Context.CLIPBOARD_SERVICE) public class ClipboardManager extends android.text.ClipboardManager { private final Context mContext; + private final Handler mHandler; private final IClipboard mService; private final ArrayList mPrimaryClipChangedListeners @@ -52,20 +56,11 @@ public class ClipboardManager extends android.text.ClipboardManager { private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener = new IOnPrimaryClipChangedListener.Stub() { - public void dispatchPrimaryClipChanged() { - mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED); - } - }; - - static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1; - - private final Handler mHandler = new Handler() { @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_REPORT_PRIMARY_CLIP_CHANGED: - reportPrimaryClipChanged(); - } + public void dispatchPrimaryClipChanged() { + mHandler.post(() -> { + reportPrimaryClipChanged(); + }); } }; @@ -89,6 +84,7 @@ public class ClipboardManager extends android.text.ClipboardManager { /** {@hide} */ public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException { mContext = context; + mHandler = handler; mService = IClipboard.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE)); } @@ -98,22 +94,38 @@ public class ClipboardManager extends android.text.ClipboardManager { * is involved in normal cut and paste operations. * * @param clip The clipped data item to set. + * @see #getPrimaryClip() + * @see #clearPrimaryClip() */ - public void setPrimaryClip(ClipData clip) { + public void setPrimaryClip(@NonNull ClipData clip) { try { - if (clip != null) { - clip.prepareToLeaveProcess(true); - } + Preconditions.checkNotNull(clip); + clip.prepareToLeaveProcess(true); mService.setPrimaryClip(clip, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** + * Clears any current primary clip on the clipboard. + * + * @see #setPrimaryClip(ClipData) + */ + public void clearPrimaryClip() { + try { + mService.clearPrimaryClip(mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Returns the current primary clip on the clipboard. + * + * @see #setPrimaryClip(ClipData) */ - public ClipData getPrimaryClip() { + public @Nullable ClipData getPrimaryClip() { try { return mService.getPrimaryClip(mContext.getOpPackageName()); } catch (RemoteException e) { @@ -124,8 +136,10 @@ public class ClipboardManager extends android.text.ClipboardManager { /** * Returns a description of the current primary clip on the clipboard * but not a copy of its data. + * + * @see #setPrimaryClip(ClipData) */ - public ClipDescription getPrimaryClipDescription() { + public @Nullable ClipDescription getPrimaryClipDescription() { try { return mService.getPrimaryClipDescription(mContext.getOpPackageName()); } catch (RemoteException e) { diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 22496a45d84d5679b4bc70f2930bf466e2791f65..10331d49dd36c58ec71b78dc80c7ea17e77e6227 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -2102,7 +2102,23 @@ public abstract class ContentResolver { Preconditions.checkNotNull(uri, "uri"); try { ActivityManager.getService().takePersistableUriPermission( - ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); + ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null, + resolveUserId(uri)); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void takePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri, + @Intent.AccessUriMode int modeFlags) { + Preconditions.checkNotNull(toPackage, "toPackage"); + Preconditions.checkNotNull(uri, "uri"); + try { + ActivityManager.getService().takePersistableUriPermission( + ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage, + resolveUserId(uri)); } catch (RemoteException e) { } } @@ -2120,7 +2136,23 @@ public abstract class ContentResolver { Preconditions.checkNotNull(uri, "uri"); try { ActivityManager.getService().releasePersistableUriPermission( - ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); + ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null, + resolveUserId(uri)); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void releasePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri, + @Intent.AccessUriMode int modeFlags) { + Preconditions.checkNotNull(toPackage, "toPackage"); + Preconditions.checkNotNull(uri, "uri"); + try { + ActivityManager.getService().releasePersistableUriPermission( + ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage, + resolveUserId(uri)); } catch (RemoteException e) { } } diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl index af0b8f0593a6732d0e5b49d58958b12082d17ce8..135a4363ef21e96c13b088556fb5c4672c480105 100644 --- a/core/java/android/content/IClipboard.aidl +++ b/core/java/android/content/IClipboard.aidl @@ -27,6 +27,7 @@ import android.content.IOnPrimaryClipChangedListener; */ interface IClipboard { void setPrimaryClip(in ClipData clip, String callingPackage); + void clearPrimaryClip(String callingPackage); ClipData getPrimaryClip(String pkg); ClipDescription getPrimaryClipDescription(String callingPackage); boolean hasPrimaryClip(String callingPackage); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e7aead17d6ed55416aa7e86c0bb3d93356f72618..ce322785c229b325d1f83c077beadc74a9c5cd36 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2448,6 +2448,23 @@ public class Intent implements Parcelable, Cloneable { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED"; + + + /** + * Broadcast Action: Sent when the current battery level changes. + * + * It has {@link android.os.BatteryManager#EXTRA_EVENTS} that carries a list of {@link Bundle} + * instances representing individual battery level changes with associated + * extras from {@link #ACTION_BATTERY_CHANGED}. + * + *

+ * This broadcast requires {@link android.Manifest.permission#BATTERY_STATS} permission. + * + * @hide + */ + @SystemApi + public static final String ACTION_BATTERY_LEVEL_CHANGED = + "android.intent.action.BATTERY_LEVEL_CHANGED"; /** * Broadcast Action: Indicates low battery condition on the device. * This broadcast corresponds to the "Low battery warning" system dialog. diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index a957aed8b806229cea4dba53de086fe261f0008b..cec3badd2e6cdd5df4f86f1b7e15f615c590aa9a 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1872,9 +1872,10 @@ public class IntentFilter implements Parcelable { du.println(sb.toString()); } } - if (mPriority != 0 || mHasPartialTypes) { + if (mPriority != 0 || mOrder != 0 || mHasPartialTypes) { sb.setLength(0); sb.append(prefix); sb.append("mPriority="); sb.append(mPriority); + sb.append(", mOrder="); sb.append(mOrder); sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes); du.println(sb.toString()); } @@ -1951,6 +1952,7 @@ public class IntentFilter implements Parcelable { dest.writeInt(mHasPartialTypes ? 1 : 0); dest.writeInt(getAutoVerify() ? 1 : 0); dest.writeInt(mInstantAppVisibility); + dest.writeInt(mOrder); } /** @@ -2020,6 +2022,7 @@ public class IntentFilter implements Parcelable { mHasPartialTypes = source.readInt() > 0; setAutoVerify(source.readInt() > 0); setVisibilityToInstantApp(source.readInt()); + mOrder = source.readInt(); } private final boolean findMimeType(String type) { diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..9f5c877e70817d2db940894beec23b9a9e85ca36 --- /dev/null +++ b/core/java/android/content/PermissionChecker.java @@ -0,0 +1,173 @@ +/* + * 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.content; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Process; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class provides permission check APIs that verify both the + * permission and the associated app op for this permission if + * such is defined. + *

+ * In the new permission model permissions with protection level + * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M} + * and above the user may not grant such permissions or revoke + * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M} + * these permissions are always granted as such apps do not expect + * permission revocations and would crash. Therefore, when the + * user disables a permission for a legacy app in the UI the + * platform disables the APIs guarded by this permission making + * them a no-op which is doing nothing or returning an empty + * result or default error. + *

+ *

+ * It is important that when you perform an operation on behalf of + * another app you use these APIs to check for permissions as the + * app may be a legacy app that does not participate in the new + * permission model for which the user had disabled the "permission" + * which is achieved by disallowing the corresponding app op. + *

+ * + * @hide + */ +public final class PermissionChecker { + /** Permission result: The permission is granted. */ + public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED; + + /** Permission result: The permission is denied. */ + public static final int PERMISSION_DENIED = PackageManager.PERMISSION_DENIED; + + /** Permission result: The permission is denied because the app op is not allowed. */ + public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1; + + /** @hide */ + @IntDef({PERMISSION_GRANTED, + PERMISSION_DENIED, + PERMISSION_DENIED_APP_OP}) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionResult {} + + private PermissionChecker() { + /* do nothing */ + } + + /** + * Checks whether a given package in a UID and PID has a given permission + * and whether the app op that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param pid The process id for which to check. + * @param uid The uid for which to check. + * @param packageName The package name for which to check. If null the + * the first package for the calling UID will be used. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkPermission(@NonNull Context context, @NonNull String permission, + int pid, int uid, @Nullable String packageName) { + if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) { + return PERMISSION_DENIED; + } + + AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); + String op = appOpsManager.permissionToOp(permission); + if (op == null) { + return PERMISSION_GRANTED; + } + + if (packageName == null) { + String[] packageNames = context.getPackageManager().getPackagesForUid(uid); + if (packageNames == null || packageNames.length <= 0) { + return PERMISSION_DENIED; + } + packageName = packageNames[0]; + } + + if (appOpsManager.noteProxyOpNoThrow(op, packageName) + != AppOpsManager.MODE_ALLOWED) { + return PERMISSION_DENIED_APP_OP; + } + + return PERMISSION_GRANTED; + } + + /** + * Checks whether your app has a given permission and whether the app op + * that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkSelfPermission(@NonNull Context context, + @NonNull String permission) { + return checkPermission(context, permission, Process.myPid(), + Process.myUid(), context.getPackageName()); + } + + /** + * Checks whether the IPC you are handling has a given permission and whether + * the app op that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param packageName The package name making the IPC. If null the + * the first package for the calling UID will be used. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkCallingPermission(@NonNull Context context, + @NonNull String permission, @Nullable String packageName) { + if (Binder.getCallingPid() == Process.myPid()) { + return PERMISSION_DENIED; + } + return checkPermission(context, permission, Binder.getCallingPid(), + Binder.getCallingUid(), packageName); + } + + /** + * Checks whether the IPC you are handling or your app has a given permission + * and whether the app op that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkCallingOrSelfPermission(@NonNull Context context, + @NonNull String permission) { + String packageName = (Binder.getCallingPid() == Process.myPid()) + ? context.getPackageName() : null; + return checkPermission(context, permission, Binder.getCallingPid(), + Binder.getCallingUid(), packageName); + } +} diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index e07fc86d697ac4e68dfd2871fd72f2452332b08e..15bfdd075ba885e9e118a7244556a607901ac15f 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -784,15 +784,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String[] resourceDirs; /** - * String retrieved from the seinfo tag found in selinux policy. This value - * can be overridden with a value set through the mac_permissions.xml policy - * construct. This value is useful in setting an SELinux security context on - * the process as well as its data directory. The String default is being used - * here to represent a catchall label when no policy matches. + * String retrieved from the seinfo tag found in selinux policy. This value can be set through + * the mac_permissions.xml policy construct. This value is used for setting an SELinux security + * context on the process as well as its data directory. * * {@hide} */ - public String seInfo = "default"; + public String seInfo; /** * The seinfo tag generated per-user. This value may change based upon the @@ -1211,6 +1209,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { if (category != CATEGORY_UNDEFINED) { pw.println(prefix + "category=" + category); } + pw.println("isAllowedToUseHiddenApi=" + isAllowedToUseHiddenApi()); } super.dumpBack(pw, prefix); } @@ -1636,8 +1635,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public boolean isAllowedToUseHiddenApi() { boolean whitelisted = SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName); - return isSystemApp() || // TODO get rid of this once the whitelist has been populated - (whitelisted && (isSystemApp() || isUpdatedSystemApp())); + return whitelisted && (isSystemApp() || isUpdatedSystemApp()); } /** diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java index 3a95a5f87d97cabba8cae6c2c8e7020c163f35b6..8184361bca80007639116159394ad7562622cf1f 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -78,8 +78,7 @@ public final class InstantAppResolveInfo implements Parcelable { private final Bundle mExtras; /** * A flag that indicates that the resolver is aware that an app may match, but would prefer - * that the installer get the sanitized intent to decide. This should not be used for - * resolutions that include a host and will be ignored in such cases. + * that the installer get the sanitized intent to decide. */ private final boolean mShouldLetInstallerDecide; @@ -96,7 +95,21 @@ public final class InstantAppResolveInfo implements Parcelable { this(digest, packageName, filters, versionCode, extras, false); } - /** Constructor for intent-based InstantApp resolution results with extras. */ + /** Constructor for intent-based InstantApp resolution results by hostname. */ + public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, + @Nullable List filters) { + this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, + null /* extras */); + } + + /** + * Constructor that indicates that resolution could be delegated to the installer when the + * sanitized intent contains enough information to resolve completely. + */ + public InstantAppResolveInfo(@Nullable Bundle extras) { + this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); + } + private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List filters, long versionCode, @Nullable Bundle extras, boolean shouldLetInstallerDecide) { @@ -118,21 +131,6 @@ public final class InstantAppResolveInfo implements Parcelable { mShouldLetInstallerDecide = shouldLetInstallerDecide; } - /** Constructor for intent-based InstantApp resolution results by hostname. */ - public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, - @Nullable List filters) { - this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, - null /* extras */); - } - - /** - * Constructor that creates a "let the installer decide" response with optional included - * extras. - */ - public InstantAppResolveInfo(@Nullable Bundle extras) { - this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); - } - InstantAppResolveInfo(Parcel in) { mShouldLetInstallerDecide = in.readBoolean(); mExtras = in.readBundle(); @@ -150,7 +148,11 @@ public final class InstantAppResolveInfo implements Parcelable { } } - /** Returns true if the installer should be notified that it should query for packages. */ + /** + * Returns true if the resolver is aware that an app may match, but would prefer + * that the installer get the sanitized intent to decide. This should not be true for + * resolutions that include a host and will be ignored in such cases. + */ public boolean shouldLetInstallerDecide() { return mShouldLetInstallerDecide; } @@ -231,6 +233,11 @@ public final class InstantAppResolveInfo implements Parcelable { @SystemApi public static final class InstantAppDigest implements Parcelable { static final int DIGEST_MASK = 0xfffff000; + + /** + * A special instance that represents and undefined digest used for cases that a host was + * not provided or is irrelevant to the response. + */ public static final InstantAppDigest UNDEFINED = new InstantAppDigest(new byte[][]{}, new int[]{}); diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 2c0c6ad0723e0aed8d38f518d43d94a909328f8b..53ffd55d5510560500c1292bc3da9fd5fda6ea9c 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -43,6 +43,14 @@ import java.util.Comparator; */ public class PackageItemInfo { private static final float MAX_LABEL_SIZE_PX = 500f; + + private static volatile boolean sForceSafeLabels = false; + + /** {@hide} */ + public static void setForceSafeLabels(boolean forceSafeLabels) { + sForceSafeLabels = forceSafeLabels; + } + /** * Public name of this item. From the "android:name" attribute. */ @@ -128,7 +136,16 @@ public class PackageItemInfo { * @return Returns a CharSequence containing the item's label. If the * item does not have a label, its name is returned. */ - public CharSequence loadLabel(PackageManager pm) { + public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) { + if (sForceSafeLabels) { + return loadSafeLabel(pm); + } else { + return loadUnsafeLabel(pm); + } + } + + /** {@hide} */ + public CharSequence loadUnsafeLabel(PackageManager pm) { if (nonLocalizedLabel != null) { return nonLocalizedLabel; } @@ -163,7 +180,7 @@ public class PackageItemInfo { @SystemApi public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) { // loadLabel() always returns non-null - String label = loadLabel(pm).toString(); + String label = loadUnsafeLabel(pm).toString(); // strip HTML tags to avoid
and other tags overwriting original message String labelStr = Html.fromHtml(label).toString(); diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 41aa9c374978a0514b741e6dd094d0d954fb2906..5b3d3e595a91c8d670af0aef4813558e4114f223 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -235,6 +235,11 @@ public abstract class PackageManagerInternal { public abstract ComponentName getHomeActivitiesAsUser(List allHomeCandidates, int userId); + /** + * @return The default home activity component name. + */ + public abstract ComponentName getDefaultHomeActivity(int userId); + /** * Called by DeviceOwnerManagerService to set the package names of device owner and profile * owners. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2420b639d678b05eab74970f9fd4cee465b1662a..5d5a9782884a7ee87f9821e0f3366053f9a87939 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1287,7 +1288,6 @@ public class PackageParser { */ @Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { - final AssetManager assets = newConfiguredAssetManager(); final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { @@ -1296,8 +1296,9 @@ public class PackageParser { } } + final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); try { - final Package pkg = parseBaseApk(apkFile, assets, flags); + final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags); pkg.setCodePath(apkFile.getCanonicalPath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; @@ -1305,26 +1306,8 @@ public class PackageParser { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to get path: " + apkFile, e); } finally { - IoUtils.closeQuietly(assets); - } - } - - private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParserException { - if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); - } - - // The AssetManager guarantees uniqueness for asset paths, so if this asset path - // already exists in the AssetManager, addAssetPath will only return the cookie - // assigned to it. - int cookie = assets.addAssetPath(apkPath); - if (cookie == 0) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + IoUtils.closeQuietly(assetLoader); } - return cookie; } private Package parseBaseApk(File apkFile, AssetManager assets, int flags) @@ -1342,13 +1325,15 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - - Resources res = null; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + final Resources res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); @@ -1383,15 +1368,18 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - final Resources res; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); + // This must always succeed, as the path has been added to the AssetManager before. + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError); @@ -1593,21 +1581,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + final ApkAssets apkAssets; + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1634,7 +1620,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + // TODO(b/72056911): Implement and call close() on ApkAssets. } } @@ -3190,7 +3176,7 @@ public class PackageParser { && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0 && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) != PermissionInfo.PROTECTION_SIGNATURE) { - outError[0] = " protectionLevel specifies a non-instnat flag but is " + outError[0] = " protectionLevel specifies a non-instant flag but is " + "not based on signature type"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3641,7 +3627,9 @@ public class PackageParser { // getting added to the wrong package. final CachedComponentArgs cachedArgs = new CachedComponentArgs(); int type; - + boolean hasActivityOrder = false; + boolean hasReceiverOrder = false; + boolean hasServiceOrder = false; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -3657,6 +3645,7 @@ public class PackageParser { return false; } + hasActivityOrder |= (a.order != 0); owner.activities.add(a); } else if (tagName.equals("receiver")) { @@ -3667,6 +3656,7 @@ public class PackageParser { return false; } + hasReceiverOrder |= (a.order != 0); owner.receivers.add(a); } else if (tagName.equals("service")) { @@ -3676,6 +3666,7 @@ public class PackageParser { return false; } + hasServiceOrder |= (s.order != 0); owner.services.add(s); } else if (tagName.equals("provider")) { @@ -3694,6 +3685,7 @@ public class PackageParser { return false; } + hasActivityOrder |= (a.order != 0); owner.activities.add(a); } else if (parser.getName().equals("meta-data")) { @@ -3827,6 +3819,15 @@ public class PackageParser { } } + if (hasActivityOrder) { + Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order)); + } + if (hasReceiverOrder) { + Collections.sort(owner.receivers, (r1, r2) -> Integer.compare(r2.order, r1.order)); + } + if (hasServiceOrder) { + Collections.sort(owner.services, (s1, s2) -> Integer.compare(s2.order, s1.order)); + } // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after // every activity info has had a chance to set it from its attributes. setMaxAspectRatio(owner); @@ -4368,6 +4369,7 @@ public class PackageParser { + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { + a.order = Math.max(intent.getOrder(), a.order); a.intents.add(intent); } // adjust activity flags when we implicitly expose it via a browsable filter @@ -4678,6 +4680,7 @@ public class PackageParser { info.windowLayout = target.info.windowLayout; info.resizeMode = target.info.resizeMode; info.maxAspectRatio = target.info.maxAspectRatio; + info.requestedVrComponent = target.info.requestedVrComponent; info.encryptionAware = info.directBootAware = target.info.directBootAware; @@ -4745,6 +4748,7 @@ public class PackageParser { + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { + a.order = Math.max(intent.getOrder(), a.order); a.intents.add(intent); } // adjust activity flags when we implicitly expose it via a browsable filter @@ -4952,6 +4956,7 @@ public class PackageParser { intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; } + outInfo.order = Math.max(intent.getOrder(), outInfo.order); outInfo.intents.add(intent); } else if (parser.getName().equals("meta-data")) { @@ -5241,6 +5246,7 @@ public class PackageParser { intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP; } + s.order = Math.max(intent.getOrder(), s.order); s.intents.add(intent); } else if (parser.getName().equals("meta-data")) { if ((s.metaData=parseMetaData(res, parser, s.metaData, @@ -5466,6 +5472,10 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0); outInfo.setPriority(priority); + int order = sa.getInt( + com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0); + outInfo.setOrder(order); + TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestIntentFilter_label); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { @@ -7053,6 +7063,8 @@ public class PackageParser { public Bundle metaData; public Package owner; + /** The order of this component in relation to its peers */ + public int order; ComponentName componentName; String componentShortName; @@ -7571,6 +7583,7 @@ public class PackageParser { for (ActivityIntentInfo aii : intents) { aii.activity = this; + order = Math.max(aii.getOrder(), order); } if (info.permission != null) { @@ -7660,6 +7673,7 @@ public class PackageParser { for (ServiceIntentInfo aii : intents) { aii.service = this; + order = Math.max(aii.getOrder(), order); } if (info.permission != null) { diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java index 99eb4702d32e4d5634997bb0265a8737500bac93..9e3a8f48996c7495b6a648367be18d27da05e269 100644 --- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java +++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java @@ -15,10 +15,13 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; @@ -26,6 +29,8 @@ import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; +import java.io.IOException; + /** * Loads the base and split APKs into a single AssetManager. * @hide @@ -33,68 +38,66 @@ import libcore.io.IoUtils; public class DefaultSplitAssetLoader implements SplitAssetLoader { private final String mBaseCodePath; private final String[] mSplitCodePaths; - private final int mFlags; - + private final @ParseFlags int mFlags; private AssetManager mCachedAssetManager; - public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) { + public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) { mBaseCodePath = pkg.baseCodePath; mSplitCodePaths = pkg.splitCodePaths; mFlags = flags; } - private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParser.PackageParserException { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); } - if (assets.addAssetPath(apkPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + try { + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { if (mCachedAssetManager != null) { return mCachedAssetManager; } - AssetManager assets = new AssetManager(); - try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - loadApkIntoAssetManager(assets, mBaseCodePath, mFlags); - - if (!ArrayUtils.isEmpty(mSplitCodePaths)) { - for (String apkPath : mSplitCodePaths) { - loadApkIntoAssetManager(assets, apkPath, mFlags); - } - } + ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null + ? mSplitCodePaths.length : 0) + 1]; - mCachedAssetManager = assets; - assets = null; - return mCachedAssetManager; - } finally { - if (assets != null) { - IoUtils.closeQuietly(assets); + // Load the base. + int splitIdx = 0; + apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags); + + // Load any splits. + if (!ArrayUtils.isEmpty(mSplitCodePaths)) { + for (String apkPath : mSplitCodePaths) { + apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags); } } + + AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + + mCachedAssetManager = assets; + return mCachedAssetManager; } @Override - public AssetManager getSplitAssetManager(int splitIdx) - throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException { return getBaseAssetManager(); } @Override public void close() throws Exception { - if (mCachedAssetManager != null) { - IoUtils.closeQuietly(mCachedAssetManager); - } + IoUtils.closeQuietly(mCachedAssetManager); } } diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java index 16023f0d9d9772612bd7e064fe41a55ad10cbb79..58eaabfa62f2ff0625ce7fdf524a4c8f3fecf16c 100644 --- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java +++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java @@ -15,17 +15,21 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.annotation.NonNull; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; import android.util.SparseArray; import libcore.io.IoUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -34,17 +38,15 @@ import java.util.Collections; * is to be used when an application opts-in to isolated split loading. * @hide */ -public class SplitAssetDependencyLoader - extends SplitDependencyLoader +public class SplitAssetDependencyLoader extends SplitDependencyLoader implements SplitAssetLoader { private final String[] mSplitPaths; - private final int mFlags; - - private String[][] mCachedPaths; - private AssetManager[] mCachedAssetManagers; + private final @ParseFlags int mFlags; + private final ApkAssets[][] mCachedSplitApks; + private final AssetManager[] mCachedAssetManagers; public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, - SparseArray dependencies, int flags) { + SparseArray dependencies, @ParseFlags int flags) { super(dependencies); // The base is inserted into index 0, so we need to shift all the splits by 1. @@ -53,7 +55,7 @@ public class SplitAssetDependencyLoader System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length); mFlags = flags; - mCachedPaths = new String[mSplitPaths.length][]; + mCachedSplitApks = new ApkAssets[mSplitPaths.length][]; mCachedAssetManagers = new AssetManager[mSplitPaths.length]; } @@ -62,58 +64,60 @@ public class SplitAssetDependencyLoader return mCachedAssetManagers[splitIdx] != null; } - private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags) - throws PackageParser.PackageParserException { - final AssetManager assets = new AssetManager(); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); + } + try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - - for (String assetPath : assetPaths) { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && - !PackageParser.isApkPath(assetPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + assetPath); - } - - if (assets.addAssetPath(assetPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + assetPath); - } - } - return assets; - } catch (Throwable e) { - IoUtils.closeQuietly(assets); - throw e; + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } + private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) { + final AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + return assets; + } + @Override protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices, - int parentSplitIdx) throws PackageParser.PackageParserException { - final ArrayList assetPaths = new ArrayList<>(); + int parentSplitIdx) throws PackageParserException { + final ArrayList assets = new ArrayList<>(); + + // Include parent ApkAssets. if (parentSplitIdx >= 0) { - Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]); + Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]); } - assetPaths.add(mSplitPaths[splitIdx]); + // Include this ApkAssets. + assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags)); + + // Load and include all config splits for this feature. for (int configSplitIdx : configSplitIndices) { - assetPaths.add(mSplitPaths[configSplitIdx]); + assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags)); } - mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]); - mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx], - mFlags); + + // Cache the results. + mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]); + mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]); } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { loadDependenciesForSplit(0); return mCachedAssetManagers[0]; } @Override - public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int idx) throws PackageParserException { // Since we insert the base at position 0, and PackageParser keeps splits separate from // the base, we need to adjust the index. loadDependenciesForSplit(idx + 1); diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 0000000000000000000000000000000000000000..9de8be3e86af5a379d0623360e93608dc3f2c6d1 --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,195 @@ +/* + * 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. + */ +package android.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets { + @GuardedBy("this") private final long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + public @NonNull String getAssetPath() { + synchronized (this) { + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + return nativeIsUpToDate(mNativePtr); + } + } + + @Override + public String toString() { + return "ApkAssets{path=" + getAssetPath() + "}"; + } + + @Override + protected void finalize() throws Throwable { + nativeDestroy(mNativePtr); + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 5f8a34d46ecd103351aca82323bc426bddbf935b..289534273d132143c0b2a00251bc20620f58ce6a 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,22 +18,33 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; -import java.io.FileDescriptor; +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -44,7 +55,20 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0]; + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + @GuardedBy("sSync") private static ArraySet sSystemApkAssetsSet; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -67,88 +91,392 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; + + /** + * A Builder class that helps create an AssetManager with only a single invocation of + * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder, + * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined + * with the user's call to add additional ApkAssets, results in multiple calls to + * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. + * @hide + */ + public static class Builder { + private ArrayList mUserApkAssets = new ArrayList<>(); + + public Builder addApkAssets(ApkAssets apkAssets) { + mUserApkAssets.add(apkAssets); + return this; + } + + public AssetManager build() { + // Retrieving the system ApkAssets forces their creation as well. + final ApkAssets[] systemApkAssets = getSystem().getApkAssets(); + + final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size(); + final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount]; + + System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length); + + final int userApkAssetCount = mUserApkAssets.size(); + for (int i = 0; i < userApkAssetCount; i++) { + apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i); + } + + // Calling this constructor prevents creation of system ApkAssets, which we took care + // of in this Builder. + final AssetManager assetManager = new AssetManager(false /*sentinel*/); + assetManager.mApkAssets = apkAssets; + AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets, + false /*invalidateCaches*/); + return assetManager; + } + } - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + */ + @GuardedBy("sSync") + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + final ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + loadStaticRuntimeOverlays(apkAssets); + + sSystemApkAssetsSet = new ArraySet<>(apkAssets); + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); + } + } + + /** + * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. + * Throws an exception if the file is corrupt or if loading the APKs referenced by the file + * fails. Returns quietly if the overlays.list file doesn't exist. + * @param outApkAssets The list to fill with the loaded ApkAssets. + */ + private static void loadStaticRuntimeOverlays(ArrayList outApkAssets) + throws IOException { + final FileInputStream fis; + try { + fis = new FileInputStream("/data/resource-cache/overlays.list"); + } catch (FileNotFoundException e) { + // We might not have any overlays, this is fine. We catch here since ApkAssets + // loading can also fail with the same exception, which we would want to propagate. + Log.i(TAG, "no overlays.list file found"); + return; + } + + try { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + // The order of this try-with-resources block matters. We must release the lock, and + // then close the file streams when exiting the block. + try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + final String idmapPath = line.split(" ")[1]; + outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } } + } finally { + // When BufferedReader is closed above, FileInputStream is closed as well. But let's be + // paranoid. + IoUtils.closeQuietly(fis); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + + ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length]; + + // Copy the system assets first. + System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length); + + // Copy the given ApkAssets if they are not already in the system list. + int newLength = sSystemApkAssets.length; + for (ApkAssets apkAsset : apkAssets) { + if (!sSystemApkAssetsSet.contains(apkAsset)) { + newApkAssets[newLength++] = apkAsset; + } + } + + // Truncate if necessary. + if (newLength != newApkAssets.length) { + newApkAssets = Arrays.copyOf(newApkAssets, newLength); + } + + synchronized (this) { + ensureOpenLocked(); + mApkAssets = newApkAssets; + nativeSetApkAssets(mObject, mApkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this + * returns a 0-length array. + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + return mApkAssets; } } + return sEmptyApkAssets; + } + + /** + * Returns a cookie for use with the other APIs of AssetManager. + * @return 0 if the path was not found, otherwise a positive integer cookie representing + * this path in the AssetManager. + * @hide + */ + public int findCookieForPath(@NonNull String path) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureValidLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (path.equals(mApkAssets[i].getAssetPath())) { + return i + 1; + } + } + } + return 0; + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + + // See if we already have it loaded. + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + mApkAssets = Arrays.copyOf(mApkAssets, count + 1); + mApkAssets[count] = assets; + nativeSetApkAssets(mObject, mApkAssets, true); + invalidateCachesLocked(-1); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + @GuardedBy("this") + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + @GuardedBy("this") + private void ensureOpenLocked() { + // If mOpen is true, this implies that mObject != 0. + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; + } } /** @@ -158,8 +486,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -174,15 +501,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -191,52 +518,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -246,26 +581,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -279,73 +636,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; - } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -786,7 +1189,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -796,132 +1204,58 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - /** - * {@hide} + * @hide */ - public native static final String getAssetAllocations(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } @GuardedBy("this") - private final void incRefsLocked(long id) { + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -931,15 +1265,117 @@ public final class AssetManager implements AutoCloseable { } @GuardedBy("this") - private final void decRefsLocked(long id) { + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; + mApkAssets = sEmptyApkAssets; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 93690bf3aef9d93e2e768c1250c9c97bbb10b6ef..19b5c45f3a812ac645f418b81c66f92ff6fef73a 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -16,10 +16,11 @@ package android.content.res; +import static android.content.ConfigurationProto.COLOR_MODE; import static android.content.ConfigurationProto.DENSITY_DPI; import static android.content.ConfigurationProto.FONT_SCALE; import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN; -import static android.content.ConfigurationProto.HDR_COLOR_MODE; +import static android.content.ConfigurationProto.KEYBOARD; import static android.content.ConfigurationProto.KEYBOARD_HIDDEN; import static android.content.ConfigurationProto.LOCALES; import static android.content.ConfigurationProto.MCC; @@ -33,7 +34,6 @@ import static android.content.ConfigurationProto.SCREEN_WIDTH_DP; import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP; import static android.content.ConfigurationProto.TOUCHSCREEN; import static android.content.ConfigurationProto.UI_MODE; -import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT; import static android.content.ConfigurationProto.WINDOW_CONFIGURATION; import static android.content.ResourcesConfigurationProto.CONFIGURATION; import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX; @@ -1095,11 +1095,9 @@ public final class Configuration implements Parcelable, Comparable> COLOR_MODE_HDR_SHIFT); - protoOutputStream.write(WIDE_COLOR_GAMUT, - colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK); + protoOutputStream.write(COLOR_MODE, colorMode); protoOutputStream.write(TOUCHSCREEN, touchscreen); + protoOutputStream.write(KEYBOARD, keyboard); protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden); protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden); protoOutputStream.write(NAVIGATION, navigation); @@ -1615,12 +1613,7 @@ public final class Configuration implements Parcelable, Comparable= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea82309e12fb945679855cb50e8712d..d4ccffb83ca5e12a9b35549bf0123eb3d7c9447a 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 7717b8d36123662cc69cb10ecf7c4d3ca3f81bfe..2a791ec9a42ee2af74e55f8cbe2f5fd830ee0a10 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -292,8 +292,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen final boolean walEnabled = (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; // Use compatibility WAL unless an app explicitly set journal/synchronous mode + // or DISABLE_COMPATIBILITY_WAL flag is set final boolean useCompatibilityWal = mConfiguration.journalMode == null - && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal; + && mConfiguration.syncMode == null + && (mConfiguration.openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0; if (walEnabled || useCompatibilityWal) { setJournalMode("WAL"); if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) { @@ -423,8 +425,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled != mConfiguration.foreignKeyConstraintsEnabled; boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags) - & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0 - || configuration.useCompatibilityWal != mConfiguration.useCompatibilityWal; + & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING + | SQLiteDatabase.DISABLE_COMPATIBILITY_WAL)) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); // Update configuration parameters. diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index dc60612451d47df9969e26b2d3294444b5db43e1..dadb95b7a7415e2d550df00289b9c56f65810a9c 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -315,7 +315,12 @@ public final class SQLiteConnectionPool implements Closeable { } } - if (mConfiguration.openFlags != configuration.openFlags) { + // We should do in-place switching when transitioning from compatibility WAL + // to rollback journal. Otherwise transient connection state will be lost + boolean onlyCompatWalChanged = (mConfiguration.openFlags ^ configuration.openFlags) + == SQLiteDatabase.DISABLE_COMPATIBILITY_WAL; + + if (!onlyCompatWalChanged && mConfiguration.openFlags != configuration.openFlags) { // If we are changing open flags and WAL mode at the same time, then // we have no choice but to close the primary connection beforehand // because there can only be one connection open when we change WAL mode. diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index ae1f57d622283db41f8e274ae0879dad786fcfc6..b463d8d0c01fd8c2d81713c7df705ab86be0604a 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -252,6 +252,13 @@ public final class SQLiteDatabase extends SQLiteClosable { */ public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; + /** + * Open flag: Flag for {@link #openDatabase} to disable Compatibility WAL when opening database. + * + * @hide + */ + public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000; + /** * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}. * @@ -288,10 +295,10 @@ public final class SQLiteDatabase extends SQLiteClosable { mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs; mConfigurationLocked.journalMode = journalMode; mConfigurationLocked.syncMode = syncMode; - mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported(); - if (!mConfigurationLocked.isInMemoryDb() && SQLiteCompatibilityWalFlags.areFlagsSet()) { - mConfigurationLocked.useCompatibilityWal = SQLiteCompatibilityWalFlags - .isCompatibilityWalSupported(); + if (!SQLiteGlobal.isCompatibilityWalSupported() || ( + SQLiteCompatibilityWalFlags.areFlagsSet() && !SQLiteCompatibilityWalFlags + .isCompatibilityWalSupported())) { + mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; } } @@ -2082,21 +2089,21 @@ public final class SQLiteDatabase extends SQLiteClosable { synchronized (mLock) { throwIfNotOpenLocked(); - final boolean oldUseCompatibilityWal = mConfigurationLocked.useCompatibilityWal; final int oldFlags = mConfigurationLocked.openFlags; - if (!oldUseCompatibilityWal && (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) { + final boolean walDisabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0; + final boolean compatibilityWalDisabled = (oldFlags & DISABLE_COMPATIBILITY_WAL) != 0; + if (walDisabled && compatibilityWalDisabled) { return; } mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING; - // If an app explicitly disables WAL, do not even use compatibility mode - mConfigurationLocked.useCompatibilityWal = false; + // If an app explicitly disables WAL, compatibility mode should be disabled too + mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; try { mConnectionPoolLocked.reconfigure(mConfigurationLocked); } catch (RuntimeException ex) { mConfigurationLocked.openFlags = oldFlags; - mConfigurationLocked.useCompatibilityWal = oldUseCompatibilityWal; throw ex; } } diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index a14df1ebcbc62a7f05e651da2fa174c4678917b7..275043f83743cf99ff8b93e386d11e6e3d12469d 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -110,15 +110,6 @@ public final class SQLiteDatabaseConfiguration { */ public long idleConnectionTimeoutMs = Long.MAX_VALUE; - /** - * Enables compatibility WAL mode. Applications cannot explicitly choose compatibility WAL mode, - * therefore it is not exposed as a flag. - * - *

In this mode, only database journal mode will be changed, connection pool - * size will still be limited to a single connection. - */ - public boolean useCompatibilityWal; - /** * Journal mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set. *

Default is returned by {@link SQLiteGlobal#getDefaultJournalMode()} @@ -191,7 +182,6 @@ public final class SQLiteDatabaseConfiguration { lookasideSlotSize = other.lookasideSlotSize; lookasideSlotCount = other.lookasideSlotCount; idleConnectionTimeoutMs = other.idleConnectionTimeoutMs; - useCompatibilityWal = other.useCompatibilityWal; journalMode = other.journalMode; syncMode = other.syncMode; } diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 64e9e5db7c4b46958fb3a7fabec7c732932fa8b5..7ff66358de5f08c21361018b829eb265f7b458ef 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -197,6 +197,8 @@ public abstract class SQLiteOpenHelper { } mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled); } + // Compatibility WAL is disabled if an app disables or enables WAL + mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL); } } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index f08e1cc24b2689c37a2126d778165fc9e22f8118..163753ee84e052d22ca7eafca9ddb582db55f821 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -33,6 +33,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSIllegalArgumentException; @@ -42,6 +43,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; +import android.os.SystemProperties; import java.io.IOException; import java.lang.ref.WeakReference; @@ -155,6 +157,10 @@ public class Camera { private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200; private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400; private static final int CAMERA_MSG_FOCUS_MOVE = 0x800; + /* ### QC ADD-ONS: START */ + private static final int CAMERA_MSG_STATS_DATA = 0x1000; + private static final int CAMERA_MSG_META_DATA = 0x2000; + /* ### QC ADD-ONS: END */ private long mNativeContext; // accessed by native methods private EventHandler mEventHandler; @@ -175,6 +181,17 @@ public class Camera { private final Object mAutoFocusCallbackLock = new Object(); private static final int NO_ERROR = 0; + private static final int EACCESS = -13; + private static final int ENODEV = -19; + private static final int EBUSY = -16; + private static final int EINVAL = -22; + private static final int ENOSYS = -38; + private static final int EUSERS = -87; + private static final int EOPNOTSUPP = -95; + /* ### QC ADD-ONS: START */ + private CameraDataCallback mCameraDataCallback; + private CameraMetaDataCallback mCameraMetaDataCallback; + /* ### QC ADD-ONS: END */ /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -249,7 +266,35 @@ public class Camera { * @return total number of accessible camera devices, or 0 if there are no * cameras or an error was encountered enumerating them. */ - public native static int getNumberOfCameras(); + public static int getNumberOfCameras() { + boolean exposeAuxCamera = false; + String packageName = ActivityThread.currentOpPackageName(); + /* Force to expose only two cameras + * if the package name does not falls in this bucket + */ + String packageList = SystemProperties.get("vendor.camera.aux.packagelist"); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + exposeAuxCamera = true; + break; + } + } + } + int numberOfCameras = _getNumberOfCameras(); + if (exposeAuxCamera == false && (numberOfCameras > 2)) { + numberOfCameras = 2; + } + return numberOfCameras; + } + + /** + * Returns the number of physical cameras available on this device. + */ + /** @hide */ + public native static int _getNumberOfCameras(); /** * Returns the information about a particular camera. @@ -260,6 +305,9 @@ public class Camera { * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { + if(cameraId >= getNumberOfCameras()){ + throw new RuntimeException("Unknown camera ID"); + } _getCameraInfo(cameraId, cameraInfo); IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); IAudioService audioService = IAudioService.Stub.asInterface(b); @@ -293,6 +341,17 @@ public class Camera { */ public static final int CAMERA_FACING_FRONT = 1; + /* ### QC ADD-ONS: START TBD*/ + /** @hide + * camera is in ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_ZSL = 2; + + /** @hide + * camera is in non-ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_NONZSL = 3; + /* ### QC ADD-ONS: END */ /** * The direction that the camera faces. It should be * CAMERA_FACING_BACK or CAMERA_FACING_FRONT. @@ -478,6 +537,10 @@ public class Camera { mPostviewCallback = null; mUsingPreviewAllocation = false; mZoomListener = null; + /* ### QC ADD-ONS: START */ + mCameraDataCallback = null; + mCameraMetaDataCallback = null; + /* ### QC ADD-ONS: END */ Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -488,8 +551,21 @@ public class Camera { mEventHandler = null; } - return native_setup(new WeakReference(this), cameraId, halVersion, - ActivityThread.currentOpPackageName()); + String packageName = ActivityThread.currentOpPackageName(); + + //Force HAL1 if the package name falls in this bucket + String packageList = SystemProperties.get("vendor.camera.hal1.packagelist", ""); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + halVersion = CAMERA_HAL_API_VERSION_1_0; + break; + } + } + } + return native_setup(new WeakReference(this), cameraId, halVersion, packageName); } private int cameraInitNormal(int cameraId) { @@ -516,6 +592,9 @@ public class Camera { /** used by Camera#open, Camera#open(int) */ Camera(int cameraId) { + if(cameraId >= getNumberOfCameras()){ + throw new RuntimeException("Unknown camera ID"); + } int err = cameraInitNormal(cameraId); if (checkInitErrors(err)) { if (err == -EACCES) { @@ -1198,7 +1277,23 @@ public class Camera { mAutoFocusMoveCallback.onAutoFocusMoving(msg.arg1 == 0 ? false : true, mCamera); } return; + /* ### QC ADD-ONS: START */ + case CAMERA_MSG_STATS_DATA: + int statsdata[] = new int[257]; + for(int i =0; i<257; i++ ) { + statsdata[i] = byteToInt( (byte[])msg.obj, i*4); + } + if (mCameraDataCallback != null) { + mCameraDataCallback.onCameraData(statsdata, mCamera); + } + return; + case CAMERA_MSG_META_DATA: + if (mCameraMetaDataCallback != null) { + mCameraMetaDataCallback.onCameraMetaData((byte[])msg.obj, mCamera); + } + return; + /* ### QC ADD-ONS: END */ default: Log.e(TAG, "Unknown message type " + msg.what); return; @@ -1870,6 +1965,23 @@ public class Camera { * as a set. Either they are all valid, or none of them are. */ public Point mouth = null; + + /** + * {@hide} + */ + public int smileDegree = 0; + /** + * {@hide} + */ + public int smileScore = 0; + /** + * {@hide} + */ + public int blinkDetected = 0; + /** + * {@hide} + */ + public int faceRecognised = 0; } /** @@ -1965,6 +2077,27 @@ public class Camera { return p; } + /** @hide + * Returns the current cct value of white balance. + * + * If it's in AWB mode, cct is determined by stats/awb module. + * + * If it's in Manual WB mode, it actually returns cct value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getWBCurrentCCT() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int cct = 0; + if (p.getWBCurrentCCT() != null) { + cct = Integer.parseInt(p.getWBCurrentCCT()); + } + + return cct; + } + /** * Returns an empty {@link Parameters} for testing purpose. * @@ -1977,6 +2110,157 @@ public class Camera { return camera.new Parameters(); } + /* ### QC ADD-ONS: START */ + private static int byteToInt(byte[] b, int offset) { + int value = 0; + for (int i = 0; i < 4; i++) { + int shift = (4 - 1 - i) * 8; + value += (b[(3-i) + offset] & 0x000000FF) << shift; + } + return value; + } + /** @hide + * Handles the callback for when Camera Data is available. + * data is read from the camera. + */ + public interface CameraDataCallback { + /** + * Callback for when camera data is available. + * + * @param data a int array of the camera data + * @param camera the Camera service object + */ + void onCameraData(int[] data, Camera camera); + }; + + /** @hide + * Set camera histogram mode and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setHistogramMode(CameraDataCallback cb) + { + mCameraDataCallback = cb; + native_setHistogramMode(cb!=null); + } + private native final void native_setHistogramMode(boolean mode); + + /** @hide + * Set camera histogram command to send data. + * + */ + public final void sendHistogramData() + { + native_sendHistogramData(); + } + private native final void native_sendHistogramData(); + + /** @hide + * Handles the callback for when Camera Meta Data is available. + * Meta data is read from the camera. + */ + public interface CameraMetaDataCallback { + /** + * Callback for when camera meta data is available. + * + * @param data a byte array of the camera meta data + * @param camera the Camera service object + */ + void onCameraMetaData(byte[] data, Camera camera); + }; + + /** @hide + * Set camera meta data and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setMetadataCb(CameraMetaDataCallback cb) + { + mCameraMetaDataCallback = cb; + native_setMetadataCb(cb!=null); + } + private native final void native_setMetadataCb(boolean mode); + + /** @hide + * Set camera face detection command to send meta data. + */ + public final void sendMetaData() + { + native_sendMetaData(); + } + private native final void native_sendMetaData(); + + /** @hide + * Configure longshot mode. Available only in ZSL. + * + * @param enable enable/disable this mode + */ + public final void setLongshot(boolean enable) + { + native_setLongshot(enable); + } + private native final void native_setLongshot(boolean enable); + + /** @hide + * Handles the Touch Co-ordinate. + */ + public class Coordinate { + /** + * Sets the x,y co-ordinates for a touch event + * + * @param x the x co-ordinate (pixels) + * @param y the y co-ordinate (pixels) + */ + public Coordinate(int x, int y) { + xCoordinate = x; + yCoordinate = y; + } + /** + * Compares {@code obj} to this co-ordinate. + * + * @param obj the object to compare this co-ordinate with. + * @return {@code true} if the xCoordinate and yCoordinate of {@code obj} is the + * same as those of this coordinate. {@code false} otherwise. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Coordinate)) { + return false; + } + Coordinate c = (Coordinate) obj; + return xCoordinate == c.xCoordinate && yCoordinate == c.yCoordinate; + } + + /** x co-ordinate for the touch event*/ + public int xCoordinate; + + /** y co-ordinate for the touch event */ + public int yCoordinate; + }; + + /** @hide + * Returns the current focus position. + * + * If it's in AF mode, it's the lens position after af is done. + * + * If it's in Manual Focus mode, it actually returns the value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getCurrentFocusPosition() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int focus_pos = -1; + if (p.getCurrentFocusPosition() != null) { + focus_pos = Integer.parseInt(p.getCurrentFocusPosition()); + } + return focus_pos; + } + + /* ### QC ADD-ONS: END */ /** * Returns a copied {@link Parameters}; for shim use only. * @@ -2224,6 +2508,10 @@ public class Camera { public static final String WHITE_BALANCE_CLOUDY_DAYLIGHT = "cloudy-daylight"; public static final String WHITE_BALANCE_TWILIGHT = "twilight"; public static final String WHITE_BALANCE_SHADE = "shade"; + /** @hide + * wb manual cct mode. + */ + public static final String WHITE_BALANCE_MANUAL_CCT = "manual-cct"; // Values for color effect settings. public static final String EFFECT_NONE = "none"; @@ -2271,6 +2559,11 @@ public class Camera { */ public static final String FLASH_MODE_TORCH = "torch"; + /** @hide + * Scene mode is off. + */ + public static final String SCENE_MODE_ASD = "asd"; + /** * Scene mode is off. */ @@ -2347,6 +2640,14 @@ public class Camera { * Capture the naturally warm color of scenes lit by candles. */ public static final String SCENE_MODE_CANDLELIGHT = "candlelight"; + /** @hide + * SCENE_MODE_BACKLIGHT + **/ + public static final String SCENE_MODE_BACKLIGHT = "backlight"; + /** @hide + * SCENE_MODE_FLOWERS + **/ + public static final String SCENE_MODE_FLOWERS = "flowers"; /** * Applications are looking for a barcode. Camera driver will be @@ -2389,6 +2690,13 @@ public class Camera { */ public static final String FOCUS_MODE_FIXED = "fixed"; + /** @hide + * Normal focus mode. Applications should call + * {@link #autoFocus(AutoFocusCallback)} to start the focus in this + * mode. + */ + public static final String FOCUS_MODE_NORMAL = "normal"; + /** * Extended depth of field (EDOF). Focusing is done digitally and * continuously. Applications should not call {@link @@ -2441,6 +2749,11 @@ public class Camera { */ public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; + /** @hide + * manual focus mode + */ + public static final String FOCUS_MODE_MANUAL_POSITION = "manual"; + // Indices for focus distance array. /** * The array index of near focus distance for use with @@ -2477,11 +2790,15 @@ public class Camera { // Formats for setPreviewFormat and setPictureFormat. private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp"; private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp"; + private static final String PIXEL_FORMAT_YUV420SP_ADRENO = "yuv420sp-adreno"; private static final String PIXEL_FORMAT_YUV422I = "yuv422i-yuyv"; private static final String PIXEL_FORMAT_YUV420P = "yuv420p"; private static final String PIXEL_FORMAT_RGB565 = "rgb565"; private static final String PIXEL_FORMAT_JPEG = "jpeg"; private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; + private static final String PIXEL_FORMAT_RAW = "raw"; + private static final String PIXEL_FORMAT_YV12 = "yv12"; + private static final String PIXEL_FORMAT_NV12 = "nv12"; /** * Order matters: Keys that are {@link #set(String, String) set} later @@ -3269,8 +3586,11 @@ public class Camera { * parameters. */ public void removeGpsData() { + remove(KEY_QC_GPS_LATITUDE_REF); remove(KEY_GPS_LATITUDE); + remove(KEY_QC_GPS_LONGITUDE_REF); remove(KEY_GPS_LONGITUDE); + remove(KEY_QC_GPS_ALTITUDE_REF); remove(KEY_GPS_ALTITUDE); remove(KEY_GPS_TIMESTAMP); remove(KEY_GPS_PROCESSING_METHOD); @@ -3535,8 +3855,8 @@ public class Camera { /** * Gets the focal length (in millimeter) of the camera. * - * @return the focal length. This method will always return a valid - * value. + * @return the focal length. Returns -1.0 when the device + * doesn't report focal length information. */ public float getFocalLength() { return Float.parseFloat(get(KEY_FOCAL_LENGTH)); @@ -4293,5 +4613,1231 @@ public class Camera { if (s1 != null && s1.equals(s2)) return true; return false; } + /* ### QC ADD-ONS: START */ + + /* ### QC ADDED PARAMETER KEYS*/ + private static final String KEY_QC_HFR_SIZE = "hfr-size"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_MODE = "preview-frame-rate-mode"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_AUTO_MODE = "frame-rate-auto"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_FIXED_MODE = "frame-rate-fixed"; + private static final String KEY_QC_GPS_LATITUDE_REF = "gps-latitude-ref"; + private static final String KEY_QC_GPS_LONGITUDE_REF = "gps-longitude-ref"; + private static final String KEY_QC_GPS_ALTITUDE_REF = "gps-altitude-ref"; + private static final String KEY_QC_GPS_STATUS = "gps-status"; + private static final String KEY_QC_EXIF_DATETIME = "exif-datetime"; + private static final String KEY_QC_TOUCH_AF_AEC = "touch-af-aec"; + private static final String KEY_QC_TOUCH_INDEX_AEC = "touch-index-aec"; + private static final String KEY_QC_TOUCH_INDEX_AF = "touch-index-af"; + private static final String KEY_QC_MANUAL_FOCUS_POSITION = "manual-focus-position"; + private static final String KEY_QC_MANUAL_FOCUS_POS_TYPE = "manual-focus-pos-type"; + private static final String KEY_QC_SCENE_DETECT = "scene-detect"; + private static final String KEY_QC_ISO_MODE = "iso"; + private static final String KEY_QC_EXPOSURE_TIME = "exposure-time"; + private static final String KEY_QC_MIN_EXPOSURE_TIME = "min-exposure-time"; + private static final String KEY_QC_MAX_EXPOSURE_TIME = "max-exposure-time"; + private static final String KEY_QC_LENSSHADE = "lensshade"; + private static final String KEY_QC_HISTOGRAM = "histogram"; + private static final String KEY_QC_SKIN_TONE_ENHANCEMENT = "skinToneEnhancement"; + private static final String KEY_QC_AUTO_EXPOSURE = "auto-exposure"; + private static final String KEY_QC_SHARPNESS = "sharpness"; + private static final String KEY_QC_MAX_SHARPNESS = "max-sharpness"; + private static final String KEY_QC_CONTRAST = "contrast"; + private static final String KEY_QC_MAX_CONTRAST = "max-contrast"; + private static final String KEY_QC_SATURATION = "saturation"; + private static final String KEY_QC_MAX_SATURATION = "max-saturation"; + private static final String KEY_QC_DENOISE = "denoise"; + private static final String KEY_QC_CONTINUOUS_AF = "continuous-af"; + private static final String KEY_QC_SELECTABLE_ZONE_AF = "selectable-zone-af"; + private static final String KEY_QC_FACE_DETECTION = "face-detection"; + private static final String KEY_QC_MEMORY_COLOR_ENHANCEMENT = "mce"; + private static final String KEY_QC_REDEYE_REDUCTION = "redeye-reduction"; + private static final String KEY_QC_ZSL = "zsl"; + private static final String KEY_QC_CAMERA_MODE = "camera-mode"; + private static final String KEY_QC_VIDEO_HIGH_FRAME_RATE = "video-hfr"; + private static final String KEY_QC_VIDEO_HDR = "video-hdr"; + private static final String KEY_QC_POWER_MODE = "power-mode"; + private static final String KEY_QC_POWER_MODE_SUPPORTED = "power-mode-supported"; + private static final String KEY_QC_WB_MANUAL_CCT = "wb-manual-cct"; + private static final String KEY_QC_MIN_WB_CCT = "min-wb-cct"; + private static final String KEY_QC_MAX_WB_CCT = "max-wb-cct"; + private static final String KEY_QC_AUTO_HDR_ENABLE = "auto-hdr-enable"; + private static final String KEY_QC_VIDEO_ROTATION = "video-rotation"; + + /** @hide + * KEY_QC_AE_BRACKET_HDR + **/ + public static final String KEY_QC_AE_BRACKET_HDR = "ae-bracket-hdr"; + + /* ### QC ADDED PARAMETER VALUES*/ + + // Values for touch af/aec settings. + /** @hide + * TOUCH_AF_AEC_OFF + **/ + public static final String TOUCH_AF_AEC_OFF = "touch-off"; + /** @hide + * TOUCH_AF_AEC_ON + **/ + public static final String TOUCH_AF_AEC_ON = "touch-on"; + + // Values for auto exposure settings. + /** @hide + * Auto exposure frame-avg + **/ + public static final String AUTO_EXPOSURE_FRAME_AVG = "frame-average"; + /** @hide + * Auto exposure center weighted + **/ + public static final String AUTO_EXPOSURE_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * Auto exposure spot metering + **/ + public static final String AUTO_EXPOSURE_SPOT_METERING = "spot-metering"; + + //Values for ISO settings + /** @hide + * ISO_AUTO + **/ + public static final String ISO_AUTO = "auto"; + /** @hide + * ISO_HJR + **/ + public static final String ISO_HJR = "ISO_HJR"; + /** @hide + * ISO_100 + **/ + public static final String ISO_100 = "ISO100"; + /** @hide + * ISO_200 + **/ + public static final String ISO_200 = "ISO200"; + /** @hide + * ISO_400 + **/ + public static final String ISO_400 = "ISO400"; + /** @hide + * ISO_800 + **/ + public static final String ISO_800 = "ISO800"; + /** @hide + * ISO_1600 + **/ + public static final String ISO_1600 = "ISO1600"; + + /** @hide + * ISO_3200 + **/ + public static final String ISO_3200 = "ISO3200"; + + //Values for Lens Shading + /** @hide + * LENSSHADE_ENABLE + **/ + public static final String LENSSHADE_ENABLE = "enable"; + /** @hide + * LENSSHADE_DISABLE + **/ + public static final String LENSSHADE_DISABLE= "disable"; + + //Values for Histogram + /** @hide + * Histogram enable + **/ + public static final String HISTOGRAM_ENABLE = "enable"; + /** @hide + * Histogram disable + **/ + public static final String HISTOGRAM_DISABLE= "disable"; + + //Values for Skin Tone Enhancement + /** @hide + * SKIN_TONE_ENHANCEMENT_ENABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_ENABLE = "enable"; + /** @hide + * SKIN_TONE_ENHANCEMENT_DISABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_DISABLE= "disable"; + + // Values for MCE settings. + /** @hide + * MCE_ENaBLE + **/ + public static final String MCE_ENABLE = "enable"; + /** @hide + * MCE_DISABLE + **/ + public static final String MCE_DISABLE = "disable"; + + // Values for ZSL settings. + /** @hide + * ZSL_ON + **/ + public static final String ZSL_ON = "on"; + /** @hide + * ZSL_OFF + **/ + public static final String ZSL_OFF = "off"; + + // Values for HDR Bracketing settings. + + /** @hide + * AEC bracketing off + **/ + public static final String AE_BRACKET_HDR_OFF = "Off"; + /** @hide + * AEC bracketing hdr + **/ + public static final String AE_BRACKET_HDR = "HDR"; + /** @hide + * AEC bracketing aec-bracket + **/ + public static final String AE_BRACKET = "AE-Bracket"; + + // Values for Power mode. + /** @hide + * LOW_POWER + **/ + public static final String LOW_POWER = "Low_Power"; + /** @hide + * NORMAL_POWER + **/ + public static final String NORMAL_POWER = "Normal_Power"; + + // Values for HFR settings. + /** @hide + * VIDEO_HFR_OFF + **/ + public static final String VIDEO_HFR_OFF = "off"; + /** @hide + * VIDEO_HFR_2X + **/ + public static final String VIDEO_HFR_2X = "60"; + /** @hide + * VIDEO_HFR_3X + **/ + public static final String VIDEO_HFR_3X = "90"; + /** @hide + * VIDEO_HFR_4X + **/ + public static final String VIDEO_HFR_4X = "120"; + + // Values for auto scene detection settings. + /** @hide + * SCENE_DETECT_OFF + **/ + public static final String SCENE_DETECT_OFF = "off"; + /** @hide + * SCENE_DETECT_ON + **/ + public static final String SCENE_DETECT_ON = "on"; + + //Values for Continuous AF + + /** @hide + * CAF off + **/ + public static final String CONTINUOUS_AF_OFF = "caf-off"; + /** @hide + * CAF on + **/ + public static final String CONTINUOUS_AF_ON = "caf-on"; + /** @hide + * Denoise off + **/ + public static final String DENOISE_OFF = "denoise-off"; + /** @hide + * Denoise on + **/ + public static final String DENOISE_ON = "denoise-on"; + + // Values for Redeye Reduction settings. + /** @hide + * REDEYE_REDUCTION_ENABLE + **/ + public static final String REDEYE_REDUCTION_ENABLE = "enable"; + /** @hide + * REDEYE_REDUCTION_DISABLE + **/ + public static final String REDEYE_REDUCTION_DISABLE = "disable"; + + // Values for selectable zone af settings. + /** @hide + * SELECTABLE_ZONE_AF_AUTO + **/ + public static final String SELECTABLE_ZONE_AF_AUTO = "auto"; + /** @hide + * SELECTABLE_ZONE_AF_SPOTMETERING + **/ + public static final String SELECTABLE_ZONE_AF_SPOTMETERING = "spot-metering"; + /** @hide + * SELECTABLE_ZONE_AF_CENTER_WEIGHTED + **/ + public static final String SELECTABLE_ZONE_AF_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * SELECTABLE_ZONE_AF_FRAME_AVERAGE + **/ + public static final String SELECTABLE_ZONE_AF_FRAME_AVERAGE = "frame-average"; + + // Values for Face Detection settings. + /** @hide + * Face Detection off + **/ + public static final String FACE_DETECTION_OFF = "off"; + /** @hide + * Face Detction on + **/ + public static final String FACE_DETECTION_ON = "on"; + + // Values for video rotation settings. + + /** @hide + * VIDEO_ROTATION_0 + **/ + public static final String VIDEO_ROTATION_0 = "0"; + /** @hide + * VIDEO_ROTATION_90 + **/ + public static final String VIDEO_ROTATION_90 = "90"; + /** @hide + * VIDEO_ROTATION_180 + **/ + public static final String VIDEO_ROTATION_180 = "180"; + /** @hide + * VIDEO_ROTATION_270 + **/ + public static final String VIDEO_ROTATION_270 = "270"; + + /* ### QC ADDED PARAMETER APIS*/ + /** @hide + * Gets the supported preview sizes in high frame rate recording mode. + * + * @return a list of Size object. This method will always return a list + * with at least one element. + */ + public List getSupportedHfrSizes() { + String str = get(KEY_QC_HFR_SIZE + SUPPORTED_VALUES_SUFFIX); + return splitSize(str); + } + + /** @hide + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + public List getSupportedTouchAfAec() { + String str = get(KEY_QC_TOUCH_AF_AEC + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + + /** @hide + * Gets the supported frame rate modes. + * + * @return a List of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public List getSupportedPreviewFrameRateModes() { + String str = get(KEY_QC_PREVIEW_FRAME_RATE_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto scene detection modes. + * + * @return a List of SCENE_DETECT_XXX string constant. null if scene detection + * setting is not supported. + * + */ + public List getSupportedSceneDetectModes() { + String str = get(KEY_QC_SCENE_DETECT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ISO values. + * + * @return a List of FLASH_MODE_XXX string constants. null if flash mode + * setting is not supported. + */ + public List getSupportedIsoValues() { + String str = get(KEY_QC_ISO_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Lensshade modes. + * + * @return a List of LENS_MODE_XXX string constants. null if lens mode + * setting is not supported. + */ + public List getSupportedLensShadeModes() { + String str = get(KEY_QC_LENSSHADE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Histogram modes. + * + * @return a List of HISTOGRAM_XXX string constants. null if histogram mode + * setting is not supported. + */ + public List getSupportedHistogramModes() { + String str = get(KEY_QC_HISTOGRAM + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Skin Tone Enhancement modes. + * + * @return a List of SKIN_TONE_ENHANCEMENT_XXX string constants. null if skin tone enhancement + * setting is not supported. + */ + public List getSupportedSkinToneEnhancementModes() { + String str = get(KEY_QC_SKIN_TONE_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto exposure setting. + * + * @return a List of AUTO_EXPOSURE_XXX string constants. null if auto exposure + * setting is not supported. + */ + public List getSupportedAutoexposure() { + String str = get(KEY_QC_AUTO_EXPOSURE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported MCE modes. + * + * @return a List of MCE_ENABLE/DISABLE string constants. null if MCE mode + * setting is not supported. + */ + public List getSupportedMemColorEnhanceModes() { + String str = get(KEY_QC_MEMORY_COLOR_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ZSL modes. + * + * @return a List of ZSL_OFF/OFF string constants. null if ZSL mode + * setting is not supported. + */ + public List getSupportedZSLModes() { + String str = get(KEY_QC_ZSL + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Video HDR modes. + * + * @return a List of Video HDR_OFF/OFF string constants. null if + * Video HDR mode setting is not supported. + */ + public List getSupportedVideoHDRModes() { + String str = get(KEY_QC_VIDEO_HDR + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported HFR modes. + * + * @return a List of VIDEO_HFR_XXX string constants. null if hfr mode + * setting is not supported. + */ + public List getSupportedVideoHighFrameRateModes() { + String str = get(KEY_QC_VIDEO_HIGH_FRAME_RATE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Continuous AF modes. + * + * @return a List of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public List getSupportedContinuousAfModes() { + String str = get(KEY_QC_CONTINUOUS_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported DENOISE modes. + * + * @return a List of DENOISE_XXX string constant. null if DENOISE + * setting is not supported. + * + */ + public List getSupportedDenoiseModes() { + String str = get(KEY_QC_DENOISE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported selectable zone af setting. + * + * @return a List of SELECTABLE_ZONE_AF_XXX string constants. null if selectable zone af + * setting is not supported. + */ + public List getSupportedSelectableZoneAf() { + String str = get(KEY_QC_SELECTABLE_ZONE_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported face detection modes. + * + * @return a List of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public List getSupportedFaceDetectionModes() { + String str = get(KEY_QC_FACE_DETECTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported redeye reduction modes. + * + * @return a List of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public List getSupportedRedeyeReductionModes() { + String str = get(KEY_QC_REDEYE_REDUCTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Sets GPS altitude reference. This will be stored in JPEG EXIF header. + * @param altRef reference GPS altitude in meters. + */ + public void setGpsAltitudeRef(double altRef) { + set(KEY_QC_GPS_ALTITUDE_REF, Double.toString(altRef)); + } + + /** @hide + * Sets GPS Status. This will be stored in JPEG EXIF header. + * + * @param status GPS status (UTC in seconds since January 1, + * 1970). + */ + public void setGpsStatus(double status) { + set(KEY_QC_GPS_STATUS, Double.toString(status)); + } + + /** @hide + * Sets the touch co-ordinate for Touch AEC. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAec(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AEC, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAec() { + String pair = get(KEY_QC_TOUCH_INDEX_AEC); + return strToCoordinate(pair); + } + + /** @hide + * Sets the touch co-ordinate for Touch AF. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAf(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AF, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAf() { + String pair = get(KEY_QC_TOUCH_INDEX_AF); + return strToCoordinate(pair); + } + /** @hide + * Set Sharpness Level + * + * @param sharpness level + */ + public void setSharpness(int sharpness){ + if((sharpness < 0) || (sharpness > getMaxSharpness()) ) + throw new IllegalArgumentException( + "Invalid Sharpness " + sharpness); + + set(KEY_QC_SHARPNESS, String.valueOf(sharpness)); + } + + /** @hide + * Set Contrast Level + * + * @param contrast level + */ + public void setContrast(int contrast){ + if((contrast < 0 ) || (contrast > getMaxContrast())) + throw new IllegalArgumentException( + "Invalid Contrast " + contrast); + + set(KEY_QC_CONTRAST, String.valueOf(contrast)); + } + + /** @hide + * Set Saturation Level + * + * @param saturation level + */ + public void setSaturation(int saturation){ + if((saturation < 0 ) || (saturation > getMaxSaturation())) + throw new IllegalArgumentException( + "Invalid Saturation " + saturation); + + set(KEY_QC_SATURATION, String.valueOf(saturation)); + } + + /** @hide + * @return true if full size video snapshot is supported. + */ + public boolean isPowerModeSupported() { + String str = get(KEY_QC_POWER_MODE_SUPPORTED); + return TRUE.equals(str); + } + + /** @hide + * Get Sharpness level + * + * @return sharpness level + */ + public int getSharpness(){ + return getInt(KEY_QC_SHARPNESS); + } + + /** @hide + * Get Max Sharpness Level + * + * @return max sharpness level + */ + public int getMaxSharpness(){ + return getInt(KEY_QC_MAX_SHARPNESS); + } + + /** @hide + * Get Contrast level + * + * @return contrast level + */ + public int getContrast(){ + return getInt(KEY_QC_CONTRAST); + } + + /** @hide + * Get Max Contrast Level + * + * @return max contrast level + */ + public int getMaxContrast(){ + return getInt(KEY_QC_MAX_CONTRAST); + } + + /** @hide + * Get Saturation level + * + * @return saturation level + */ + public int getSaturation(){ + return getInt(KEY_QC_SATURATION); + } + + /** @hide + * Get Max Saturation Level + * + * @return max contrast level + */ + public int getMaxSaturation(){ + return getInt(KEY_QC_MAX_SATURATION); + } + + /** @hide + * Sets GPS latitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param latRef GPS latitude reference coordinate. + */ + public void setGpsLatitudeRef(String latRef) { + set(KEY_QC_GPS_LATITUDE_REF, latRef); + } + + /** @hide + * Sets GPS longitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param lonRef GPS longitude reference coordinate. + */ + public void setGpsLongitudeRef(String lonRef) { + set(KEY_QC_GPS_LONGITUDE_REF, lonRef); + } + + /** @hide + * Sets system timestamp. This will be stored in JPEG EXIF header. + * + * @param dateTime current timestamp (UTC in seconds since January 1, + * 1970). + */ + public void setExifDateTime(String dateTime) { + set(KEY_QC_EXIF_DATETIME, dateTime); + } + + /** @hide + * Gets the current Touch AF/AEC setting. + * + * @return one of TOUCH_AF_AEC_XXX string constant. null if Touch AF/AEC + * setting is not supported. + * + */ + public String getTouchAfAec() { + return get(KEY_QC_TOUCH_AF_AEC); + } + + /** @hide + * Sets the current TOUCH AF/AEC setting. + * + * @param value TOUCH_AF_AEC_XXX string constants. + * + */ + public void setTouchAfAec(String value) { + set(KEY_QC_TOUCH_AF_AEC, value); + } + + /** @hide + * Gets the current redeye reduction setting. + * + * @return one of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public String getRedeyeReductionMode() { + return get(KEY_QC_REDEYE_REDUCTION); + } + + /** @hide + * Sets the redeye reduction. Other parameters may be changed after changing + * redeye reduction. After setting redeye reduction, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value REDEYE_REDUCTION_XXX string constants. + * + */ + public void setRedeyeReductionMode(String value) { + set(KEY_QC_REDEYE_REDUCTION, value); + } + + /** @hide + * Gets the frame rate mode setting. + * + * @return one of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public String getPreviewFrameRateMode() { + return get(KEY_QC_PREVIEW_FRAME_RATE_MODE); + } + + /** @hide + * Sets the frame rate mode. + * + * @param value FRAME_RATE_XXX_MODE string constants. + */ + public void setPreviewFrameRateMode(String value) { + set(KEY_QC_PREVIEW_FRAME_RATE_MODE, value); + } + + /** @hide + * Gets the current auto scene detection setting. + * + * @return one of SCENE_DETECT_XXX string constant. null if auto scene detection + * setting is not supported. + * + */ + public String getSceneDetectMode() { + return get(KEY_QC_SCENE_DETECT); + } + + /** @hide + * Sets the auto scene detect. Other parameters may be changed after changing + * scene detect. After setting auto scene detection, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value SCENE_DETECT_XXX string constants. + * + */ + public void setSceneDetectMode(String value) { + set(KEY_QC_SCENE_DETECT, value); + } + + /** @hide + * Gets the current hdr bracketing mode setting. + * + * @return current hdr bracketing mode. + * @see #KEY_AE_BRACKET_OFF + * @see #KEY_AE_BRACKET_HDR + * @see #KEY_AE_BRACKET_BRACKATING + */ + public String getAEBracket() { + return get(KEY_QC_AE_BRACKET_HDR); + } + + /** @hide + * Sets the Power mode. + * + * @param value Power mode. + * @see #getPowerMode() + */ + public void setPowerMode(String value) { + set(KEY_QC_POWER_MODE, value); + } + + /** @hide + * Gets the current power mode setting. + * + * @return current power mode. null if power mode setting is not + * supported. + * @see #POWER_MODE_LOW + * @see #POWER_MODE_NORMAL + */ + public String getPowerMode() { + return get(KEY_QC_POWER_MODE); + } + + /** @hide + * Set HDR-Bracketing Level + * + * @param value HDR-Bracketing + */ + public void setAEBracket(String value){ + set(KEY_QC_AE_BRACKET_HDR, value); + } + + /** @hide + * Gets the current ISO setting. + * + * @return one of ISO_XXX string constant. null if ISO + * setting is not supported. + */ + public String getISOValue() { + return get(KEY_QC_ISO_MODE); + } + + /** @hide + * Sets the ISO. + * + * @param iso ISO_XXX string constant. + */ + public void setISOValue(String iso) { + set(KEY_QC_ISO_MODE, iso); + } + + /** @hide + * Sets the exposure time. + * + * @param value exposure time. + */ + public void setExposureTime(int value) { + set(KEY_QC_EXPOSURE_TIME, Integer.toString(value)); + } + + /** @hide + * Gets the current exposure time. + * + * @return exposure time. + */ + public String getExposureTime() { + return get(KEY_QC_EXPOSURE_TIME); + } + + /** @hide + * Gets the min supported exposure time. + * + * @return min supported exposure time. + */ + public String getMinExposureTime() { + return get(KEY_QC_MIN_EXPOSURE_TIME); + } + + /** @hide + * Gets the max supported exposure time. + * + * @return max supported exposure time. + */ + public String getMaxExposureTime() { + return get(KEY_QC_MAX_EXPOSURE_TIME); + } + + /** @hide + * Gets the current LensShade Mode. + * + * @return LensShade Mode + */ + public String getLensShade() { + return get(KEY_QC_LENSSHADE); + } + + /** @hide + * Sets the current LensShade Mode. + * + * @return LensShade Mode + */ + public void setLensShade(String lensshade) { + set(KEY_QC_LENSSHADE, lensshade); + } + + /** @hide + * Gets the current auto exposure setting. + * + * @return one of AUTO_EXPOSURE_XXX string constant. null if auto exposure + * setting is not supported. + */ + public String getAutoExposure() { + return get(KEY_QC_AUTO_EXPOSURE); + } + + /** @hide + * Sets the current auto exposure setting. + * + * @param value AUTO_EXPOSURE_XXX string constants. + */ + public void setAutoExposure(String value) { + set(KEY_QC_AUTO_EXPOSURE, value); + } + + /** @hide + * Gets the current MCE Mode. + * + * @return MCE value + */ + public String getMemColorEnhance() { + return get(KEY_QC_MEMORY_COLOR_ENHANCEMENT); + } + + /** @hide + * Sets the current MCE Mode. + * + * @return MCE Mode + */ + public void setMemColorEnhance(String mce) { + set(KEY_QC_MEMORY_COLOR_ENHANCEMENT, mce); + } + + /** @hide + * Set white balance manual cct value. + * + * @param cct user CCT setting. + */ + public void setWBManualCCT(int cct) { + set(KEY_QC_WB_MANUAL_CCT, Integer.toString(cct)); + } + + /** @hide + * Gets the WB min supported CCT. + * + * @return min cct value. + */ + public String getWBMinCCT() { + return get(KEY_QC_MIN_WB_CCT); + } + + /** @hide + * Gets the WB max supported CCT. + * + * @return max cct value. + */ + public String getMaxWBCCT() { + return get(KEY_QC_MAX_WB_CCT); + } + + /** @hide + * Gets the current WB CCT. + * + * @return CCT value + */ + public String getWBCurrentCCT() { + return get(KEY_QC_WB_MANUAL_CCT); + } + + /** @hide + * Gets the current ZSL Mode. + * + * @return ZSL mode value + */ + public String getZSLMode() { + return get(KEY_QC_ZSL); + } + + /** @hide + * Sets the current ZSL Mode. ZSL mode is set as a 0th bit in KEY_CAMERA_MODE. + * + * @return null + */ + public void setZSLMode(String zsl) { + set(KEY_QC_ZSL, zsl); + } + + /** @hide + * Sets the current Auto HDR Mode. + * @ auto_hdr auto hdr string for enable/disable + * @return null + */ + public void setAutoHDRMode(String auto_hdr){ + set(KEY_QC_AUTO_HDR_ENABLE,auto_hdr); + } + + /** @hide + * Gets the current Camera Mode Flag. Camera mode includes a + * flag(byte) which indicates different camera modes. + * For now support for ZSL added at bit0 + * + * @return Camera Mode. + */ + public String getCameraMode() { + return get(KEY_QC_CAMERA_MODE); + } + + /** @hide + * Sets the current Camera Mode. + * + * @return null + */ + public void setCameraMode(int cameraMode) { + set(KEY_QC_CAMERA_MODE, cameraMode); + } + + private static final int MANUAL_FOCUS_POS_TYPE_INDEX = 0; + private static final int MANUAL_FOCUS_POS_TYPE_DAC = 1; + /** @hide + * Set focus position. + * + * @param pos user setting of focus position. + */ + public void setFocusPosition(int type, int pos) { + set(KEY_QC_MANUAL_FOCUS_POS_TYPE, Integer.toString(type)); + set(KEY_QC_MANUAL_FOCUS_POSITION, Integer.toString(pos)); + } + + /** @hide + * Gets the current focus position. + * + * @return current focus position + */ + public String getCurrentFocusPosition() { + return get(KEY_QC_MANUAL_FOCUS_POSITION); + } + + + /** @hide + * Gets the current HFR Mode. + * + * @return VIDEO_HFR_XXX string constants + */ + public String getVideoHighFrameRate() { + return get(KEY_QC_VIDEO_HIGH_FRAME_RATE); + } + + /** @hide + * Sets the current HFR Mode. + * + * @param hfr VIDEO_HFR_XXX string constants + */ + public void setVideoHighFrameRate(String hfr) { + set(KEY_QC_VIDEO_HIGH_FRAME_RATE, hfr); + } + + /** @hide + * Gets the current Video HDR Mode. + * + * @return Video HDR mode value + */ + public String getVideoHDRMode() { + return get(KEY_QC_VIDEO_HDR); + } + + /** @hide + * Sets the current Video HDR Mode. + * + * @return null + */ + public void setVideoHDRMode(String videohdr) { + set(KEY_QC_VIDEO_HDR, videohdr); + } + + /** @hide + * Gets the current DENOISE setting. + * + * @return one of DENOISE_XXX string constant. null if Denoise + * setting is not supported. + * + */ + public String getDenoise() { + return get(KEY_QC_DENOISE); + } + + /** @hide + * Gets the current Continuous AF setting. + * + * @return one of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public String getContinuousAf() { + return get(KEY_QC_CONTINUOUS_AF); + } + + /** @hide + * Sets the current Denoise mode. + * @param value DENOISE_XXX string constants. + * + */ + + public void setDenoise(String value) { + set(KEY_QC_DENOISE, value); + } + + /** @hide + * Sets the current Continuous AF mode. + * @param value CONTINUOUS_AF_XXX string constants. + * + */ + public void setContinuousAf(String value) { + set(KEY_QC_CONTINUOUS_AF, value); + } + + /** @hide + * Gets the current selectable zone af setting. + * + * @return one of SELECTABLE_ZONE_AF_XXX string constant. null if selectable zone af + * setting is not supported. + */ + public String getSelectableZoneAf() { + return get(KEY_QC_SELECTABLE_ZONE_AF); + } + + /** @hide + * Sets the current selectable zone af setting. + * + * @param value SELECTABLE_ZONE_AF_XXX string constants. + */ + public void setSelectableZoneAf(String value) { + set(KEY_QC_SELECTABLE_ZONE_AF, value); + } + + /** @hide + * Gets the current face detection setting. + * + * @return one of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public String getFaceDetectionMode() { + return get(KEY_QC_FACE_DETECTION); + } + + /** @hide + * Sets the auto scene detect. Other settings like Touch AF/AEC might be + * changed after setting face detection. + * + * @param value FACE_DETECTION_XXX string constants. + * + */ + public void setFaceDetectionMode(String value) { + set(KEY_QC_FACE_DETECTION, value); + } + + /** @hide + * Gets the current video rotation setting. + * + * @return one of VIDEO_QC_ROTATION_XXX string constant. null if video rotation + * setting is not supported. + */ + public String getVideoRotation() { + return get(KEY_QC_VIDEO_ROTATION); + } + + /** @hide + * Sets the current video rotation setting. + * + * @param value VIDEO_QC_ROTATION_XXX string constants. + */ + public void setVideoRotation(String value) { + set(KEY_QC_VIDEO_ROTATION, value); + } + /** @hide + * Gets the supported video rotation modes. + * + * @return a List of VIDEO_QC_ROTATION_XXX string constant. null if this + * setting is not supported. + */ + public List getSupportedVideoRotationValues() { + String str = get(KEY_QC_VIDEO_ROTATION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + // Splits a comma delimited string to an ArrayList of Coordinate. + // Return null if the passing string is null or the Coordinate is 0. + private ArrayList splitCoordinate(String str) { + if (str == null) return null; + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(str); + ArrayList coordinateList = new ArrayList(); + for (String s : splitter) { + Coordinate coordinate = strToCoordinate(s); + if (coordinate != null) coordinateList.add(coordinate); + } + if (coordinateList.size() == 0) return null; + return coordinateList; + } + + // Parses a string (ex: "500x500") to Coordinate object. + // Return null if the passing string is null. + private Coordinate strToCoordinate(String str) { + if (str == null) return null; + + int pos = str.indexOf('x'); + if (pos != -1) { + String x = str.substring(0, pos); + String y = str.substring(pos + 1); + return new Coordinate(Integer.parseInt(x), + Integer.parseInt(y)); + } + Log.e(TAG, "Invalid Coordinate parameter string=" + str); + return null; + } + /* ### QC ADD-ONS: END */ }; } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index a2bc91e0cda6adefd7ca9986cb5b6e6368fd14a5..207a869dd4497084ced31cab3d5079c42604b6f3 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.app.ActivityThread; import android.content.Context; import android.hardware.CameraInfo; import android.hardware.CameraStatus; @@ -37,6 +38,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; import android.util.ArrayMap; import android.util.Log; @@ -839,8 +842,22 @@ public final class CameraManager { // Try to make sure we have an up-to-date list of camera devices. connectCameraServiceLocked(); + boolean exposeAuxCamera = false; + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("vendor.camera.aux.packagelist"); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + exposeAuxCamera = true; + break; + } + } + } int idCount = 0; for (int i = 0; i < mDeviceStatus.size(); i++) { + if(!exposeAuxCamera && (i == 2)) break; int status = mDeviceStatus.valueAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT || status == ICameraServiceListener.STATUS_ENUMERATING) continue; @@ -849,6 +866,7 @@ public final class CameraManager { cameraIds = new String[idCount]; idCount = 0; for (int i = 0; i < mDeviceStatus.size(); i++) { + if(!exposeAuxCamera && (i == 2)) break; int status = mDeviceStatus.valueAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT || status == ICameraServiceListener.STATUS_ENUMERATING) continue; @@ -866,6 +884,26 @@ public final class CameraManager { throw new IllegalArgumentException("cameraId was null"); } + /* Force to expose only two cameras + * if the package name does not falls in this bucket + */ + boolean exposeAuxCamera = false; + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("vendor.camera.aux.packagelist"); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + exposeAuxCamera = true; + break; + } + } + } + if (exposeAuxCamera == false && (Integer.parseInt(cameraId) >= 2)) { + throw new IllegalArgumentException("invalid cameraId"); + } + ICameraService cameraService = getCameraService(); if (cameraService == null) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, @@ -985,6 +1023,30 @@ public final class CameraManager { } private void onStatusChangedLocked(int status, String id) { + /* Force to ignore the last mono/aux camera status update + * if the package name does not falls in this bucket + */ + boolean exposeMonoCamera = false; + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("vendor.camera.aux.packagelist"); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + exposeMonoCamera = true; + break; + } + } + } + + if (exposeMonoCamera == false) { + if (Integer.parseInt(id) >= 2) { + Log.w(TAG, "[soar.cts] ignore the status update of camera: " + id); + return; + } + } + if (DEBUG) { Log.v(TAG, String.format("Camera id %s has status changed to 0x%x", id, status)); @@ -1060,6 +1122,31 @@ public final class CameraManager { String.format("Camera id %s has torch status changed to 0x%x", id, status)); } + /* Force to ignore the aux or composite camera torch status update + * if the package name does not falls in this bucket + */ + boolean exposeMonoCamera = false; + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("vendor.camera.aux.packagelist"); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + exposeMonoCamera = true; + break; + } + } + } + + if (exposeMonoCamera == false) { + if (Integer.parseInt(id) >= 2) { + Log.w(TAG, "ignore the torch status update of camera: " + id); + return; + } + } + + if (!validTorchStatus(status)) { Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, status)); diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index ada7ebf0f5dacdf1f5050d3e7f59faa8ec148d84..98901a1e69190bf801efb90af139b2b2cf989627 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1457,7 +1457,8 @@ public final class CaptureRequest extends CameraMetadata> * is used, all non-zero weights will have the same effect. A region with 0 weight is * ignored.

*

If all regions have 0 weight, then no specific metering area needs to be used by the - * camera device.

+ * camera device. The capture result will either be a zero weight region as well, or + * the region selected by the camera device as the focus area of interest.

*

If the metering region is outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in * capture result metadata, the camera device will ignore the sections outside the crop * region and output only the intersection rectangle as the metering region in the result diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 8c2f8c1e85534f136dadf8fdcd903d17090255e8..e14dfa88fb8bf85df052136924bca7d0a4eb6804 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -1156,7 +1156,8 @@ public class CaptureResult extends CameraMetadata> { * is used, all non-zero weights will have the same effect. A region with 0 weight is * ignored.

*

If all regions have 0 weight, then no specific metering area needs to be used by the - * camera device.

+ * camera device. The capture result will either be a zero weight region as well, or + * the region selected by the camera device as the focus area of interest.

*

If the metering region is outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in * capture result metadata, the camera device will ignore the sections outside the crop * region and output only the intersection rectangle as the metering region in the result diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 8b8bbc34f7d258080a2f273bb58c857adf52cf5d..0a597984e2827e153f3f9506a4e4a121149cdd8f 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -165,7 +165,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } else if (request.isReprocess() && !isReprocessable()) { throw new IllegalArgumentException("this capture session cannot handle reprocess " + "requests"); - } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { + } else if (!mDeviceImpl.isPrivilegedApp() && + request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 511fa43a544132761dce9e5e558b090ef62c4767..889eab1826d7b709779db7514a11f76f3b513c0a 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -16,9 +16,12 @@ package android.hardware.camera2.impl; +import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; import android.hardware.ICameraService; +import android.app.ActivityThread; +import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; @@ -41,6 +44,8 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; import android.util.Range; import android.util.Size; @@ -118,6 +123,7 @@ public class CameraDeviceImpl extends CameraDevice private int mNextSessionId = 0; private final int mAppTargetSdkVersion; + private boolean mIsPrivilegedApp = false; // Runnables for all state transitions, except error, which needs the // error code argument @@ -260,6 +266,7 @@ public class CameraDeviceImpl extends CameraDevice } else { mTotalPartialCount = partialCount; } + mIsPrivilegedApp = checkPrivilegedAppList(); } public CameraDeviceCallbacks getCallbacks() { @@ -1174,11 +1181,41 @@ public class CameraDeviceImpl extends CameraDevice } } + private boolean checkPrivilegedAppList() { + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("persist.vendor.camera.privapp.list"); + + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + return true; + } + } + } + + return false; + } + + public boolean isPrivilegedApp() { + return mIsPrivilegedApp; + } + private void checkInputConfiguration(InputConfiguration inputConfig) { if (inputConfig != null) { StreamConfigurationMap configMap = mCharacteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + /* + * don't check input format and size, + * if the package name is in the white list + */ + if (isPrivilegedApp()) { + Log.w(TAG, "ignore input format/size check for white listed app"); + return; + } + int[] inputFormats = configMap.getInputFormats(); boolean validFormat = false; for (int format : inputFormats) { diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 6b2059e1c84c74fb091b7b9d43265501d03eacdd..e81fbeed34b033a7259e15470b7a85b7d0a3a5cd 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -28,7 +28,6 @@ import android.content.Context; import android.graphics.Point; import android.media.projection.MediaProjection; import android.os.Handler; -import android.os.UserHandle; import android.util.SparseArray; import android.view.Display; import android.view.Surface; @@ -615,6 +614,7 @@ public final class DisplayManager { * @hide */ @SystemApi + @TestApi public Point getStableDisplaySize() { return mGlobal.getStableDisplaySize(); } @@ -635,6 +635,8 @@ public final class DisplayManager { * * @hide until we make it a system api */ + @SystemApi + @TestApi @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public List getAmbientBrightnessStats() { return mGlobal.getAmbientBrightnessStats(); diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java index f85ce3eed82e3841ae91e836c6d093942b9d38c8..e1c69d717a8d78e353ac63ab57c2b0b0b552df2a 100644 --- a/core/java/android/hardware/location/ContextHubMessage.java +++ b/core/java/android/hardware/location/ContextHubMessage.java @@ -33,7 +33,7 @@ import java.util.Arrays; */ @SystemApi @Deprecated -public class ContextHubMessage { +public class ContextHubMessage implements Parcelable { private static final int DEBUG_LOG_NUM_BYTES = 16; private int mType; private int mVersion; diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java index b5c01ec214eb058ffb66e5396389d1c94400e2ae..ded1bb8c4c44749f7470454521b62551448911cd 100644 --- a/core/java/android/hardware/location/NanoApp.java +++ b/core/java/android/hardware/location/NanoApp.java @@ -36,7 +36,7 @@ import android.util.Log; */ @SystemApi @Deprecated -public class NanoApp { +public class NanoApp implements Parcelable { private final String TAG = "NanoApp"; private final String UNKNOWN = "Unknown"; diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java index 75a96ee8c802c64fccda6c4011b7f1ad134a477f..4d8e7344cd758e006d1e94daf27131ca4412e1e4 100644 --- a/core/java/android/hardware/location/NanoAppFilter.java +++ b/core/java/android/hardware/location/NanoAppFilter.java @@ -28,7 +28,7 @@ import android.os.Parcelable; */ @SystemApi @Deprecated -public class NanoAppFilter { +public class NanoAppFilter implements Parcelable { private static final String TAG = "NanoAppFilter"; diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java index f1926eaa2195bfd944e6ccdea830bc725e252e77..75fb915708deb2bbfe6a362bea3bb880c2d81915 100644 --- a/core/java/android/hardware/location/NanoAppInstanceInfo.java +++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java @@ -34,7 +34,7 @@ import libcore.util.EmptyArray; */ @SystemApi @Deprecated -public class NanoAppInstanceInfo { +public class NanoAppInstanceInfo implements Parcelable { private String mPublisher = "Unknown"; private String mName = "Unknown"; diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 0294a29b828216bf119d0ed59be8639eb486a1e4..2a878ebba0469760f1721b80c57441c2452f6337 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -441,6 +441,15 @@ public final class ProgramSelector implements Parcelable { */ public static @NonNull ProgramSelector createAmFmSelector( @RadioManager.Band int band, int frequencyKhz, int subChannel) { + if (band == RadioManager.BAND_INVALID) { + // 50MHz is a rough boundary between AM (<30MHz) and FM (>60MHz). + if (frequencyKhz < 50000) { + band = (subChannel <= 0) ? RadioManager.BAND_AM : RadioManager.BAND_AM_HD; + } else { + band = (subChannel <= 0) ? RadioManager.BAND_FM : RadioManager.BAND_FM_HD; + } + } + boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD); boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD); if (!isAm && !isDigital && band != RadioManager.BAND_FM) { diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java index 3cc4b566a3c02b3e61ce649dc86cdb1de097d6c3..6e510607754a471a384989fe1dba94a9d14f3979 100644 --- a/core/java/android/hardware/radio/RadioMetadata.java +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -96,6 +96,48 @@ public final class RadioMetadata implements Parcelable { */ public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK"; + /** + * Technology-independent program name (station name). + */ + public static final String METADATA_KEY_PROGRAM_NAME = + "android.hardware.radio.metadata.PROGRAM_NAME"; + + /** + * DAB ensemble name. + */ + public static final String METADATA_KEY_DAB_ENSEMBLE_NAME = + "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME"; + + /** + * DAB ensemble name - short version (up to 8 characters). + */ + public static final String METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT = + "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME_SHORT"; + + /** + * DAB service name. + */ + public static final String METADATA_KEY_DAB_SERVICE_NAME = + "android.hardware.radio.metadata.DAB_SERVICE_NAME"; + + /** + * DAB service name - short version (up to 8 characters). + */ + public static final String METADATA_KEY_DAB_SERVICE_NAME_SHORT = + "android.hardware.radio.metadata.DAB_SERVICE_NAME_SHORT"; + + /** + * DAB component name. + */ + public static final String METADATA_KEY_DAB_COMPONENT_NAME = + "android.hardware.radio.metadata.DAB_COMPONENT_NAME"; + + /** + * DAB component name. + */ + public static final String METADATA_KEY_DAB_COMPONENT_NAME_SHORT = + "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT"; + private static final int METADATA_TYPE_INVALID = -1; private static final int METADATA_TYPE_INT = 0; @@ -119,6 +161,13 @@ public final class RadioMetadata implements Parcelable { METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK); + METADATA_KEYS_TYPE.put(METADATA_KEY_PROGRAM_NAME, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME_SHORT, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME_SHORT, METADATA_TYPE_TEXT); } // keep in sync with: system/media/radio/include/system/radio_metadata.h diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 017674f5ea8ab8db1a66bca27013badc0d53ffe0..b3bf092aeffebc7534e752473af7aa8c5e658758 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1074,25 +1074,6 @@ public class InputMethodService extends AbstractInputMethodService { return mInputConnection; } - /** - * Force switch to a new input method component. This can only be called - * from an application or a service which has a token of the currently active input method. - * @param id The unique identifier for the new input method to be switched to. - */ - public void setInputMethod(String id) { - mImm.setInputMethodInternal(mToken, id); - } - - /** - * Force switch to a new input method and subtype. This can only be called - * from an application or a service which has a token of the currently active input method. - * @param id The unique identifier for the new input method to be switched to. - * @param subtype The new subtype of the new input method to be switched to. - */ - public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) { - mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype); - } - /** * Force switch to the last used input method and subtype. If the last input method didn't have * any subtypes, the framework will simply switch to the last input method with no subtype @@ -1100,8 +1081,8 @@ public class InputMethodService extends AbstractInputMethodService { * @return true if the current input method and subtype was successfully switched to the last * used input method and subtype. */ - public boolean switchToLastInputMethod() { - return mImm.switchToLastInputMethodInternal(mToken); + public final boolean switchToPreviousInputMethod() { + return mImm.switchToPreviousInputMethodInternal(mToken); } /** @@ -1112,7 +1093,7 @@ public class InputMethodService extends AbstractInputMethodService { * @return true if the current input method and subtype was successfully switched to the next * input method and subtype. */ - public boolean switchToNextInputMethod(boolean onlyCurrentIme) { + public final boolean switchToNextInputMethod(boolean onlyCurrentIme) { return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme); } @@ -1125,7 +1106,7 @@ public class InputMethodService extends AbstractInputMethodService { * and subtype in order to provide the consistent user experience in switching * between IMEs and subtypes. */ - public boolean shouldOfferSwitchingToNextInputMethod() { + public final boolean shouldOfferSwitchingToNextInputMethod() { return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken); } @@ -1455,12 +1436,24 @@ public class InputMethodService extends AbstractInputMethodService { * input method will be destroyed, and the requested one started on the * current input field. * - * @param id Unique identifier of the new input method ot start. + * @param id Unique identifier of the new input method to start. */ public void switchInputMethod(String id) { mImm.setInputMethodInternal(mToken, id); } + /** + * Force switch to a new input method, as identified by {@code id}. This + * input method will be destroyed, and the requested one started on the + * current input field. + * + * @param id Unique identifier of the new input method to start. + * @param subtype The new subtype of the new input method to be switched to. + */ + public final void switchInputMethod(String id, InputMethodSubtype subtype) { + mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype); + } + public void setExtractView(View view) { mExtractFrame.removeAllViews(); mExtractFrame.addView(view, new FrameLayout.LayoutParams( @@ -2066,7 +2059,7 @@ public class InputMethodService extends AbstractInputMethodService { * @see InputMethodManager#SHOW_FORCED * @param flags Provides additional operating flags. */ - public void requestShowSelf(int flags) { + public final void requestShowSelf(int flags) { mImm.showSoftInputFromInputMethodInternal(mToken, flags); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index b4c4ea095c789fabb91c966309b67e5da6de820d..94ea19e689479e8c802b2b44fe05bebe2feb2774 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -460,133 +460,177 @@ public class ConnectivityManager { public static final int TYPE_NONE = -1; /** - * The Mobile data connection. When active, all data traffic - * will use this network type's interface by default - * (it has a default route) + * A Mobile data connection. Devices may support more than one. + * + * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an + * appropriate network. {@see NetworkCapabilities} for supported transports. */ + @Deprecated public static final int TYPE_MOBILE = 0; + /** - * The WIFI data connection. When active, all data traffic - * will use this network type's interface by default - * (it has a default route). + * A WIFI data connection. Devices may support more than one. + * + * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an + * appropriate network. {@see NetworkCapabilities} for supported transports. */ + @Deprecated public static final int TYPE_WIFI = 1; + /** * An MMS-specific Mobile data connection. This network type may use the * same network interface as {@link #TYPE_MOBILE} or it may use a different * one. This is used by applications needing to talk to the carrier's * Multimedia Messaging Service servers. * - * @deprecated Applications should instead use + * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability. */ @Deprecated public static final int TYPE_MOBILE_MMS = 2; + /** * A SUPL-specific Mobile data connection. This network type may use the * same network interface as {@link #TYPE_MOBILE} or it may use a different * one. This is used by applications needing to talk to the carrier's * Secure User Plane Location servers for help locating the device. * - * @deprecated Applications should instead use + * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability. */ @Deprecated public static final int TYPE_MOBILE_SUPL = 3; + /** * A DUN-specific Mobile data connection. This network type may use the * same network interface as {@link #TYPE_MOBILE} or it may use a different * one. This is sometimes by the system when setting up an upstream connection * for tethering so that the carrier is aware of DUN traffic. + * + * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that + * provides the {@link NetworkCapabilities#NET_CAPABILITY_DUN} capability. */ + @Deprecated public static final int TYPE_MOBILE_DUN = 4; + /** * A High Priority Mobile data connection. This network type uses the * same network interface as {@link #TYPE_MOBILE} but the routing setup * is different. * - * @deprecated Applications should instead use - * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that - * uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport. + * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an + * appropriate network. {@see NetworkCapabilities} for supported transports. */ @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; + /** - * The WiMAX data connection. When active, all data traffic - * will use this network type's interface by default - * (it has a default route). + * A WiMAX data connection. + * + * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an + * appropriate network. {@see NetworkCapabilities} for supported transports. */ + @Deprecated public static final int TYPE_WIMAX = 6; /** - * The Bluetooth data connection. When active, all data traffic - * will use this network type's interface by default - * (it has a default route). + * A Bluetooth data connection. + * + * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an + * appropriate network. {@see NetworkCapabilities} for supported transports. */ + @Deprecated public static final int TYPE_BLUETOOTH = 7; /** * Dummy data connection. This should not be used on shipping devices. + * @deprecated This is not used any more. */ + @Deprecated public static final int TYPE_DUMMY = 8; /** - * The Ethernet data connection. When active, all data traffic - * will use this network type's interface by default - * (it has a default route). + * An Ethernet data connection. + * + * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an + * appropriate network. {@see NetworkCapabilities} for supported transports. */ + @Deprecated public static final int TYPE_ETHERNET = 9; /** * Over the air Administration. + * @deprecated Use {@link NetworkCapabilities} instead. * {@hide} */ + @Deprecated public static final int TYPE_MOBILE_FOTA = 10; /** * IP Multimedia Subsystem. + * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IMS} instead. * {@hide} */ + @Deprecated public static final int TYPE_MOBILE_IMS = 11; /** * Carrier Branded Services. + * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_CBS} instead. * {@hide} */ + @Deprecated public static final int TYPE_MOBILE_CBS = 12; /** * A Wi-Fi p2p connection. Only requesting processes will have access to * the peers connected. + * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_WIFI_P2P} instead. * {@hide} */ + @Deprecated public static final int TYPE_WIFI_P2P = 13; /** * The network to use for initially attaching to the network + * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IA} instead. * {@hide} */ + @Deprecated public static final int TYPE_MOBILE_IA = 14; /** * Emergency PDN connection for emergency services. This * may include IMS and MMS in emergency situations. + * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_EIMS} instead. * {@hide} */ + @Deprecated public static final int TYPE_MOBILE_EMERGENCY = 15; /** * The network that uses proxy to achieve connectivity. + * @deprecated Use {@link NetworkCapabilities} instead. * {@hide} */ + @Deprecated public static final int TYPE_PROXY = 16; /** * A virtual network using one or more native bearers. * It may or may not be providing security services. + * @deprecated Applications should use {@link NetworkCapabilities#TRANSPORT_VPN} instead. */ + @Deprecated public static final int TYPE_VPN = 17; /** {@hide} */ @@ -693,8 +737,10 @@ public class ConnectivityManager { * @param type the type needing naming * @return a String for the given type, or a string version of the type ("87") * if no name is known. + * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead. * {@hide} */ + @Deprecated public static String getNetworkTypeName(int type) { switch (type) { case TYPE_NONE: @@ -745,8 +791,10 @@ public class ConnectivityManager { * This should be replaced in the future by a network property. * @param networkType the type to check * @return a boolean - {@code true} if uses cellular network, else {@code false} + * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead. * {@hide} */ + @Deprecated public static boolean isNetworkTypeMobile(int networkType) { switch (networkType) { case TYPE_MOBILE: @@ -768,8 +816,10 @@ public class ConnectivityManager { /** * Checks if the given network type is backed by a Wi-Fi radio. * + * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead. * @hide */ + @Deprecated public static boolean isNetworkTypeWifi(int networkType) { switch (networkType) { case TYPE_WIFI: @@ -1536,6 +1586,8 @@ public class ConnectivityManager { * IllegalArgumentException if no mapping from the legacy type to * NetworkCapabilities is known. * + * @deprecated Types are deprecated. Use {@link NetworkCallback} or {@link NetworkRequest} + * to find the network instead. * @hide */ public static NetworkCapabilities networkCapabilitiesForType(int type) { @@ -2387,6 +2439,7 @@ public class ConnectivityManager { * * @param networkType The type of network you want to report on * @param percentage The quality of the connection 0 is bad, 100 is good + * @deprecated Types are deprecated. Use {@link #reportNetworkConnectivity} instead. * {@hide} */ public void reportInetCondition(int networkType, int percentage) { @@ -2518,9 +2571,10 @@ public class ConnectivityManager { * * @param networkType The network type we'd like to check * @return {@code true} if supported, else {@code false} - * + * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead. * @hide */ + @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isNetworkSupported(int networkType) { try { diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index 6e2654e3ce7c45f6bfefabef240b0d88e080603a..4631c565962f92742ad5b908e01ae75d2a921eee 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -25,6 +25,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; +import java.util.Comparator; /** * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a @@ -186,6 +187,20 @@ public final class IpPrefix implements Parcelable { return Arrays.equals(this.address, addrBytes); } + /** + * Returns whether the specified prefix is entirely contained in this prefix. + * + * Note this is mathematical inclusion, so a prefix is always contained within itself. + * @param otherPrefix the prefix to test + * @hide + */ + public boolean containsPrefix(IpPrefix otherPrefix) { + if (otherPrefix.getPrefixLength() < prefixLength) return false; + final byte[] otherAddress = otherPrefix.getRawAddress(); + NetworkUtils.maskRawAddress(otherAddress, prefixLength); + return Arrays.equals(otherAddress, address); + } + /** * @hide */ @@ -229,6 +244,38 @@ public final class IpPrefix implements Parcelable { dest.writeInt(prefixLength); } + /** + * Returns a comparator ordering IpPrefixes by length, shorter to longer. + * Contents of the address will break ties. + * @hide + */ + public static Comparator lengthComparator() { + return new Comparator() { + @Override + public int compare(IpPrefix prefix1, IpPrefix prefix2) { + if (prefix1.isIPv4()) { + if (prefix2.isIPv6()) return -1; + } else { + if (prefix2.isIPv4()) return 1; + } + final int p1len = prefix1.getPrefixLength(); + final int p2len = prefix2.getPrefixLength(); + if (p1len < p2len) return -1; + if (p2len < p1len) return 1; + final byte[] a1 = prefix1.address; + final byte[] a2 = prefix2.address; + final int len = a1.length < a2.length ? a1.length : a2.length; + for (int i = 0; i < len; ++i) { + if (a1[i] < a2[i]) return -1; + if (a1[i] > a2[i]) return 1; + } + if (a2.length < len) return 1; + if (a1.length < len) return -1; + return 0; + } + }; + } + /** * Implement the Parcelable interface. */ diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index bae373d7564b6793591167c93606ffa5b11cae53..ef58f94e70cfb89cadd595baef10fa36543bf57e 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.net.ConnectivityManager.NetworkCallback; import android.os.Parcel; import android.os.Parcelable; @@ -69,6 +70,7 @@ public final class NetworkCapabilities implements Parcelable { mSignalStrength = nc.mSignalStrength; mUids = nc.mUids; mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; + mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; } } @@ -78,7 +80,7 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void clearAll() { - mNetworkCapabilities = mTransportTypes = 0; + mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; @@ -92,6 +94,11 @@ public final class NetworkCapabilities implements Parcelable { */ private long mNetworkCapabilities; + /** + * If any capabilities specified here they must not exist in the matching Network. + */ + private long mUnwantedNetworkCapabilities; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "NET_CAPABILITY_" }, value = { @@ -117,6 +124,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_FOREGROUND, NET_CAPABILITY_NOT_CONGESTED, NET_CAPABILITY_NOT_SUSPENDED, + NET_CAPABILITY_OEM_PAID, }) public @interface NetCapability { } @@ -264,8 +272,15 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; + /** + * Indicates that traffic that goes through this network is paid by oem. For example, + * this network can be used by system apps to upload telemetry data. + * @hide + */ + public static final int NET_CAPABILITY_OEM_PAID = 22; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PAID; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -313,7 +328,8 @@ public final class NetworkCapabilities implements Parcelable { (1 << NET_CAPABILITY_IA) | (1 << NET_CAPABILITY_IMS) | (1 << NET_CAPABILITY_RCS) | - (1 << NET_CAPABILITY_XCAP); + (1 << NET_CAPABILITY_XCAP) | + (1 << NET_CAPABILITY_OEM_PAID); /** * Capabilities that suggest that a network is unrestricted. @@ -330,31 +346,55 @@ public final class NetworkCapabilities implements Parcelable { * Adds the given capability to this {@code NetworkCapability} instance. * Multiple capabilities may be applied sequentially. Note that when searching * for a network to satisfy a request, all capabilities requested must be satisfied. + *

+ * If the given capability was previously added to the list of unwanted capabilities + * then the capability will also be removed from the list of unwanted capabilities. * * @param capability the capability to be added. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public NetworkCapabilities addCapability(@NetCapability int capability) { - if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { - throw new IllegalArgumentException("NetworkCapability out of range"); - } + checkValidCapability(capability); mNetworkCapabilities |= 1 << capability; + mUnwantedNetworkCapabilities &= ~(1 << capability); // remove from unwanted capability list return this; } + /** + * Adds the given capability to the list of unwanted capabilities of this + * {@code NetworkCapability} instance. Multiple unwanted capabilities may be applied + * sequentially. Note that when searching for a network to satisfy a request, the network + * must not contain any capability from unwanted capability list. + *

+ * If the capability was previously added to the list of required capabilities (for + * example, it was there by default or added using {@link #addCapability(int)} method), then + * it will be removed from the list of required capabilities as well. + * + * @see #addCapability(int) + * @hide + */ + public void addUnwantedCapability(@NetCapability int capability) { + checkValidCapability(capability); + mUnwantedNetworkCapabilities |= 1 << capability; + mNetworkCapabilities &= ~(1 << capability); // remove from requested capabilities + } + /** * Removes (if found) the given capability from this {@code NetworkCapability} instance. + *

+ * Note that this method removes capabilities that was added via {@link #addCapability(int)}, + * {@link #addUnwantedCapability(int)} or {@link #setCapabilities(int[], int[])} . * * @param capability the capability to be removed. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public NetworkCapabilities removeCapability(@NetCapability int capability) { - if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { - throw new IllegalArgumentException("NetworkCapability out of range"); - } - mNetworkCapabilities &= ~(1 << capability); + checkValidCapability(capability); + final long mask = ~(1 << capability); + mNetworkCapabilities &= mask; + mUnwantedNetworkCapabilities &= mask; return this; } @@ -383,31 +423,58 @@ public final class NetworkCapabilities implements Parcelable { return BitUtils.unpackBits(mNetworkCapabilities); } + /** + * Gets all the unwanted capabilities set on this {@code NetworkCapability} instance. + * + * @return an array of unwanted capability values for this instance. + * @hide + */ + public @NetCapability int[] getUnwantedCapabilities() { + return BitUtils.unpackBits(mUnwantedNetworkCapabilities); + } + + /** * Sets all the capabilities set on this {@code NetworkCapability} instance. * This overwrites any existing capabilities. * * @hide */ - public void setCapabilities(@NetCapability int[] capabilities) { + public void setCapabilities(@NetCapability int[] capabilities, + @NetCapability int[] unwantedCapabilities) { mNetworkCapabilities = BitUtils.packBits(capabilities); + mUnwantedNetworkCapabilities = BitUtils.packBits(unwantedCapabilities); + } + + /** + * @deprecated use {@link #setCapabilities(int[], int[])} + * @hide + */ + @Deprecated + public void setCapabilities(@NetCapability int[] capabilities) { + setCapabilities(capabilities, new int[] {}); } /** - * Tests for the presence of a capabilitity on this instance. + * Tests for the presence of a capability on this instance. * * @param capability the capabilities to be tested for. * @return {@code true} if set on this instance. */ public boolean hasCapability(@NetCapability int capability) { - if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { - return false; - } - return ((mNetworkCapabilities & (1 << capability)) != 0); + return isValidCapability(capability) + && ((mNetworkCapabilities & (1 << capability)) != 0); + } + + /** @hide */ + public boolean hasUnwantedCapability(@NetCapability int capability) { + return isValidCapability(capability) + && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0); } private void combineNetCapabilities(NetworkCapabilities nc) { this.mNetworkCapabilities |= nc.mNetworkCapabilities; + this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities; } /** @@ -418,7 +485,9 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public String describeFirstNonRequestableCapability() { - final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES); + final long nonRequestable = (mNetworkCapabilities | mUnwantedNetworkCapabilities) + & NON_REQUESTABLE_CAPABILITIES; + if (nonRequestable != 0) { return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]); } @@ -428,21 +497,29 @@ public final class NetworkCapabilities implements Parcelable { } private boolean satisfiedByNetCapabilities(NetworkCapabilities nc, boolean onlyImmutable) { - long networkCapabilities = this.mNetworkCapabilities; + long requestedCapabilities = mNetworkCapabilities; + long requestedUnwantedCapabilities = mUnwantedNetworkCapabilities; + long providedCapabilities = nc.mNetworkCapabilities; + if (onlyImmutable) { - networkCapabilities = networkCapabilities & ~MUTABLE_CAPABILITIES; + requestedCapabilities &= ~MUTABLE_CAPABILITIES; + requestedUnwantedCapabilities &= ~MUTABLE_CAPABILITIES; } - return ((nc.mNetworkCapabilities & networkCapabilities) == networkCapabilities); + return ((providedCapabilities & requestedCapabilities) == requestedCapabilities) + && ((requestedUnwantedCapabilities & providedCapabilities) == 0); } /** @hide */ public boolean equalsNetCapabilities(NetworkCapabilities nc) { - return (nc.mNetworkCapabilities == this.mNetworkCapabilities); + return (nc.mNetworkCapabilities == this.mNetworkCapabilities) + && (nc.mUnwantedNetworkCapabilities == this.mUnwantedNetworkCapabilities); } private boolean equalsNetCapabilitiesRequestable(NetworkCapabilities that) { return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) == - (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)); + (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)) + && ((this.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) == + (that.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)); } /** @@ -882,7 +959,16 @@ public final class NetworkCapabilities implements Parcelable { /** * List of UIDs this network applies to. No restriction if null. *

- * This is typically (and at this time, only) used by VPN. This network is only available to + * For networks, mUids represent the list of network this applies to, and null means this + * network applies to all UIDs. + * For requests, mUids is the list of UIDs this network MUST apply to to match ; ALL UIDs + * must be included in a network so that they match. As an exception to the general rule, + * a null mUids field for requests mean "no requirements" rather than what the general rule + * would suggest ("must apply to all UIDs") : this is because this has shown to be what users + * of this API expect in practice. A network that must match all UIDs can still be + * expressed with a set ranging the entire set of possible UIDs. + *

+ * mUids is typically (and at this time, only) used by VPN. This network is only available to * the UIDs in this list, and it is their default network. Apps in this list that wish to * bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this * member is null, then the network is not restricted by app UID. If it's an empty list, then @@ -1004,8 +1090,7 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public boolean satisfiedByUids(NetworkCapabilities nc) { - if (null == nc.mUids) return true; // The network satisfies everything. - if (null == mUids) return false; // Not everything allowed but requires everything + if (null == nc.mUids || null == mUids) return true; // The network satisfies everything. for (UidRange requiredRange : mUids) { if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true; if (!nc.appliesToUidRange(requiredRange)) { @@ -1168,15 +1253,17 @@ public final class NetworkCapabilities implements Parcelable { @Override public int hashCode() { - return ((int) (mNetworkCapabilities & 0xFFFFFFFF) + return (int) (mNetworkCapabilities & 0xFFFFFFFF) + ((int) (mNetworkCapabilities >> 32) * 3) - + ((int) (mTransportTypes & 0xFFFFFFFF) * 5) - + ((int) (mTransportTypes >> 32) * 7) - + (mLinkUpBandwidthKbps * 11) - + (mLinkDownBandwidthKbps * 13) - + Objects.hashCode(mNetworkSpecifier) * 17 - + (mSignalStrength * 19) - + Objects.hashCode(mUids) * 23); + + ((int) (mUnwantedNetworkCapabilities & 0xFFFFFFFF) * 5) + + ((int) (mUnwantedNetworkCapabilities >> 32) * 7) + + ((int) (mTransportTypes & 0xFFFFFFFF) * 11) + + ((int) (mTransportTypes >> 32) * 13) + + (mLinkUpBandwidthKbps * 17) + + (mLinkDownBandwidthKbps * 19) + + Objects.hashCode(mNetworkSpecifier) * 23 + + (mSignalStrength * 29) + + Objects.hashCode(mUids) * 31; } @Override @@ -1186,6 +1273,7 @@ public final class NetworkCapabilities implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mNetworkCapabilities); + dest.writeLong(mUnwantedNetworkCapabilities); dest.writeLong(mTransportTypes); dest.writeInt(mLinkUpBandwidthKbps); dest.writeInt(mLinkDownBandwidthKbps); @@ -1201,6 +1289,7 @@ public final class NetworkCapabilities implements Parcelable { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.mNetworkCapabilities = in.readLong(); + netCap.mUnwantedNetworkCapabilities = in.readLong(); netCap.mTransportTypes = in.readLong(); netCap.mLinkUpBandwidthKbps = in.readInt(); netCap.mLinkDownBandwidthKbps = in.readInt(); @@ -1218,34 +1307,73 @@ public final class NetworkCapabilities implements Parcelable { @Override public String toString() { - // TODO: enumerate bits for transports and capabilities instead of creating arrays. - // TODO: use a StringBuilder instead of string concatenation. - int[] types = getTransportTypes(); - String transports = (types.length > 0) ? " Transports: " + transportNamesOf(types) : ""; - - types = getCapabilities(); - String capabilities = (types.length > 0 ? " Capabilities: " : ""); - for (int i = 0; i < types.length; ) { - capabilities += capabilityNameOf(types[i]); - if (++i < types.length) capabilities += "&"; + final StringBuilder sb = new StringBuilder("["); + if (0 != mTransportTypes) { + sb.append(" Transports: "); + appendStringRepresentationOfBitMaskToStringBuilder(sb, mTransportTypes, + NetworkCapabilities::transportNameOf, "|"); + } + if (0 != mNetworkCapabilities) { + sb.append(" Capabilities: "); + appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities, + NetworkCapabilities::capabilityNameOf, "&"); + } + if (0 != mNetworkCapabilities) { + sb.append(" Unwanted: "); + appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities, + NetworkCapabilities::capabilityNameOf, "&"); + } + if (mLinkUpBandwidthKbps > 0) { + sb.append(" LinkUpBandwidth>=").append(mLinkUpBandwidthKbps).append("Kbps"); + } + if (mLinkDownBandwidthKbps > 0) { + sb.append(" LinkDnBandwidth>=").append(mLinkDownBandwidthKbps).append("Kbps"); + } + if (mNetworkSpecifier != null) { + sb.append(" Specifier: <").append(mNetworkSpecifier).append(">"); + } + if (hasSignalStrength()) { + sb.append(" SignalStrength: ").append(mSignalStrength); } - String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" + - mLinkUpBandwidthKbps + "Kbps" : ""); - String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" + - mLinkDownBandwidthKbps + "Kbps" : ""); - - String specifier = (mNetworkSpecifier == null ? - "" : " Specifier: <" + mNetworkSpecifier + ">"); - - String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : ""); + if (null != mUids) { + if ((1 == mUids.size()) && (mUids.valueAt(0).count() == 1)) { + sb.append(" Uid: ").append(mUids.valueAt(0).start); + } else { + sb.append(" Uids: <").append(mUids).append(">"); + } + } + if (mEstablishingVpnAppUid != INVALID_UID) { + sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid); + } - String uids = (null != mUids ? " Uids: <" + mUids + ">" : ""); + sb.append("]"); + return sb.toString(); + } - String establishingAppUid = " EstablishingAppUid: " + mEstablishingVpnAppUid; - return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength - + uids + establishingAppUid + "]"; + private interface NameOf { + String nameOf(int value); + } + /** + * @hide + */ + public static void appendStringRepresentationOfBitMaskToStringBuilder(StringBuilder sb, + long bitMask, NameOf nameFetcher, String separator) { + int bitPos = 0; + boolean firstElementAdded = false; + while (bitMask != 0) { + if ((bitMask & 1) != 0) { + if (firstElementAdded) { + sb.append(separator); + } else { + firstElementAdded = true; + } + sb.append(nameFetcher.nameOf(bitPos)); + } + bitMask >>= 1; + ++bitPos; + } } /** @hide */ @@ -1313,6 +1441,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_FOREGROUND: return "FOREGROUND"; case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED"; case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED"; + case NET_CAPABILITY_OEM_PAID: return "OEM_PAID"; default: return Integer.toString(capability); } } @@ -1344,4 +1473,13 @@ public final class NetworkCapabilities implements Parcelable { Preconditions.checkArgument( isValidTransport(transport), "Invalid TransportType " + transport); } + + private static boolean isValidCapability(@NetworkCapabilities.NetCapability int capability) { + return capability >= MIN_NET_CAPABILITY && capability <= MAX_NET_CAPABILITY; + } + + private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) { + Preconditions.checkArgument(isValidCapability(capability), + "NetworkCapability " + capability + "out of range"); + } } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index e6ad89a20bbd1d6e88cd72a30b69d2e671818ae6..999771a18ec0cf2c5b807d5e7b73d490227f876b 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -38,14 +38,18 @@ public class NetworkInfo implements Parcelable { * * * - * + * * * + * + * + * * + * * * - * * + * *
Detailed stateCoarse-grained state
IDLEDISCONNECTED
SCANNINGCONNECTING
SCANNINGDISCONNECTED
CONNECTINGCONNECTING
AUTHENTICATINGCONNECTING
OBTAINING_IPADDRCONNECTING
VERIFYING_POOR_LINKCONNECTING
CAPTIVE_PORTAL_CHECKCONNECTING
CONNECTEDCONNECTED
SUSPENDEDSUSPENDED
DISCONNECTINGDISCONNECTING
DISCONNECTEDDISCONNECTED
UNAVAILABLEDISCONNECTED
FAILEDDISCONNECTED
BLOCKEDDISCONNECTED
*/ public enum State { @@ -163,8 +167,17 @@ public class NetworkInfo implements Parcelable { * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link * ConnectivityManager#TYPE_ETHERNET}, {@link ConnectivityManager#TYPE_BLUETOOTH}, or other - * types defined by {@link ConnectivityManager} + * types defined by {@link ConnectivityManager}. + * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport} + * instead with one of the NetworkCapabilities#TRANSPORT_* constants : + * {@link #getType} and {@link #getTypeName} cannot account for networks using + * multiple transports. Note that generally apps should not care about transport; + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and + * {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that + * apps concerned with meteredness or bandwidth should be looking at, as they + * offer this information with much better accuracy. */ + @Deprecated public int getType() { synchronized (this) { return mNetworkType; @@ -172,8 +185,10 @@ public class NetworkInfo implements Parcelable { } /** + * @deprecated Use {@link NetworkCapabilities} instead * @hide */ + @Deprecated public void setType(int type) { synchronized (this) { mNetworkType = type; @@ -205,7 +220,16 @@ public class NetworkInfo implements Parcelable { * Return a human-readable name describe the type of the network, * for example "WIFI" or "MOBILE". * @return the name of the network type + * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport} + * instead with one of the NetworkCapabilities#TRANSPORT_* constants : + * {@link #getType} and {@link #getTypeName} cannot account for networks using + * multiple transports. Note that generally apps should not care about transport; + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and + * {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that + * apps concerned with meteredness or bandwidth should be looking at, as they + * offer this information with much better accuracy. */ + @Deprecated public String getTypeName() { synchronized (this) { return mTypeName; @@ -230,7 +254,15 @@ public class NetworkInfo implements Parcelable { * that the network is fully usable. * @return {@code true} if network connectivity exists or is in the process * of being established, {@code false} otherwise. + * @deprecated Apps should instead use the + * {@link android.net.ConnectivityManager.NetworkCallback} API to + * learn about connectivity changes. + * {@link ConnectivityManager#registerDefaultNetworkCallback} and + * {@link ConnectivityManager#registerNetworkCallback}. These will + * give a more accurate picture of the connectivity state of + * the device and let apps react more easily and quickly to changes. */ + @Deprecated public boolean isConnectedOrConnecting() { synchronized (this) { return mState == State.CONNECTED || mState == State.CONNECTING; @@ -259,8 +291,18 @@ public class NetworkInfo implements Parcelable { * data roaming has been disabled. *

  • The device's radio is turned off, e.g., because airplane mode is enabled.
  • * + * Since Android L, this always returns {@code true}, because the system only + * returns info for available networks. * @return {@code true} if the network is available, {@code false} otherwise + * @deprecated Apps should instead use the + * {@link android.net.ConnectivityManager.NetworkCallback} API to + * learn about connectivity changes. + * {@link ConnectivityManager#registerDefaultNetworkCallback} and + * {@link ConnectivityManager#registerNetworkCallback}. These will + * give a more accurate picture of the connectivity state of + * the device and let apps react more easily and quickly to changes. */ + @Deprecated public boolean isAvailable() { synchronized (this) { return mIsAvailable; @@ -270,9 +312,11 @@ public class NetworkInfo implements Parcelable { /** * Sets if the network is available, ie, if the connectivity is possible. * @param isAvailable the new availability value. + * @deprecated Use {@link NetworkCapabilities} instead * * @hide */ + @Deprecated public void setIsAvailable(boolean isAvailable) { synchronized (this) { mIsAvailable = isAvailable; @@ -285,7 +329,10 @@ public class NetworkInfo implements Parcelable { * network following a disconnect from another network. * @return {@code true} if this is a failover attempt, {@code false} * otherwise. + * @deprecated This field is not populated in recent Android releases, + * and does not make a lot of sense in a multi-network world. */ + @Deprecated public boolean isFailover() { synchronized (this) { return mIsFailover; @@ -296,8 +343,10 @@ public class NetworkInfo implements Parcelable { * Set the failover boolean. * @param isFailover {@code true} to mark the current connection attempt * as a failover. + * @deprecated This hasn't been set in any recent Android release. * @hide */ + @Deprecated public void setFailover(boolean isFailover) { synchronized (this) { mIsFailover = isFailover; @@ -322,7 +371,10 @@ public class NetworkInfo implements Parcelable { } } - /** {@hide} */ + /** + * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING} instead. + * {@hide} + */ @VisibleForTesting @Deprecated public void setRoaming(boolean isRoaming) { @@ -334,7 +386,15 @@ public class NetworkInfo implements Parcelable { /** * Reports the current coarse-grained state of the network. * @return the coarse-grained state + * @deprecated Apps should instead use the + * {@link android.net.ConnectivityManager.NetworkCallback} API to + * learn about connectivity changes. + * {@link ConnectivityManager#registerDefaultNetworkCallback} and + * {@link ConnectivityManager#registerNetworkCallback}. These will + * give a more accurate picture of the connectivity state of + * the device and let apps react more easily and quickly to changes. */ + @Deprecated public State getState() { synchronized (this) { return mState; @@ -358,8 +418,10 @@ public class NetworkInfo implements Parcelable { * if one was supplied. May be {@code null}. * @param extraInfo an optional {@code String} providing addditional network state * information passed up from the lower networking layers. + * @deprecated Use {@link NetworkCapabilities} instead. * @hide */ + @Deprecated public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) { synchronized (this) { this.mDetailedState = detailedState; @@ -385,6 +447,8 @@ public class NetworkInfo implements Parcelable { * Report the reason an attempt to establish connectivity failed, * if one is available. * @return the reason for failure, or null if not available + * @deprecated This method does not have a consistent contract that could make it useful + * to callers. */ public String getReason() { synchronized (this) { diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 9f47f62775e188eb42150fb5af6a409ed9c09e3b..1a28732e874e26a1536465d0a3ece78d3df154f2 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -257,7 +257,7 @@ public class NetworkPolicy implements Parcelable, Comparable { public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException, BackupUtils.BadVersionException { final int version = in.readInt(); - if (version > VERSION_RAPID) { + if (version < VERSION_INIT || version > VERSION_RAPID) { throw new BackupUtils.BadVersionException("Unknown backup version: " + version); } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index a0724091abeeca7e41e59ecf8a5dc161eb424f95..fdcc304e1d2e3cdcb8cf173505f4e2049714b8b3 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.text.TextUtils; import android.util.proto.ProtoOutputStream; @@ -132,12 +133,18 @@ public class NetworkRequest implements Parcelable { * needed in terms of {@link NetworkCapabilities} features */ public static class Builder { - private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities(); + private final NetworkCapabilities mNetworkCapabilities; /** * Default constructor for Builder. */ - public Builder() {} + public Builder() { + // By default, restrict this request to networks available to this app. + // Apps can rescind this restriction, but ConnectivityService will enforce + // it for apps that do not have the NETWORK_SETTINGS permission. + mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.setSingleUid(Process.myUid()); + } /** * Build {@link NetworkRequest} give the current set of capabilities. @@ -158,6 +165,9 @@ public class NetworkRequest implements Parcelable { * the requested network's required capabilities. Note that when searching * for a network to satisfy a request, all capabilities requested must be * satisfied. + *

    + * If the given capability was previously added to the list of unwanted capabilities + * then the capability will also be removed from the list of unwanted capabilities. * * @param capability The capability to add. * @return The builder to facilitate chaining @@ -169,7 +179,8 @@ public class NetworkRequest implements Parcelable { } /** - * Removes (if found) the given capability from this builder instance. + * Removes (if found) the given capability from this builder instance from both required + * and unwanted capabilities lists. * * @param capability The capability to remove. * @return The builder to facilitate chaining. @@ -193,6 +204,24 @@ public class NetworkRequest implements Parcelable { return this; } + /** + * Add a capability that must not exist in the requested network. + *

    + * If the capability was previously added to the list of required capabilities (for + * example, it was there by default or added using {@link #addCapability(int)} method), then + * it will be removed from the list of required capabilities as well. + * + * @see #addCapability(int) + * + * @param capability The capability to add to unwanted capability list. + * @return The builder to facilitate chaining. + * @hide + */ + public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) { + mNetworkCapabilities.addUnwantedCapability(capability); + return this; + } + /** * Completely clears all the {@code NetworkCapabilities} from this builder instance, * removing even the capabilities that are set by default when the object is constructed. diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index fe9563d6d32547ec5205b3fd502a326579b73996..9a5d502673f37adf00ce6af20be25ddd8251aec6 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -16,19 +16,20 @@ package android.net; +import android.os.Parcel; +import android.util.Log; +import android.util.Pair; + import java.io.FileDescriptor; -import java.net.InetAddress; +import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; +import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Collection; import java.util.Locale; - -import android.os.Parcel; -import android.util.Log; -import android.util.Pair; - +import java.util.TreeSet; /** * Native methods for managing network interfaces. @@ -385,4 +386,72 @@ public class NetworkUtils { result = builder.toString(); return result; } + + /** + * Returns a prefix set without overlaps. + * + * This expects the src set to be sorted from shorter to longer. Results are undefined + * failing this condition. The returned prefix set is sorted in the same order as the + * passed set, with the same comparator. + */ + private static TreeSet deduplicatePrefixSet(final TreeSet src) { + final TreeSet dst = new TreeSet<>(src.comparator()); + // Prefixes match addresses that share their upper part up to their length, therefore + // the only kind of possible overlap in two prefixes is strict inclusion of the longer + // (more restrictive) in the shorter (including equivalence if they have the same + // length). + // Because prefixes in the src set are sorted from shorter to longer, deduplicating + // is done by simply iterating in order, and not adding any longer prefix that is + // already covered by a shorter one. + newPrefixes: + for (IpPrefix newPrefix : src) { + for (IpPrefix existingPrefix : dst) { + if (existingPrefix.containsPrefix(newPrefix)) { + continue newPrefixes; + } + } + dst.add(newPrefix); + } + return dst; + } + + /** + * Returns how many IPv4 addresses match any of the prefixes in the passed ordered set. + * + * Obviously this returns an integral value between 0 and 2**32. + * The behavior is undefined if any of the prefixes is not an IPv4 prefix or if the + * set is not ordered smallest prefix to longer prefix. + * + * @param prefixes the set of prefixes, ordered by length + */ + public static long routedIPv4AddressCount(final TreeSet prefixes) { + long routedIPCount = 0; + for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) { + if (!prefix.isIPv4()) { + Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount"); + } + int rank = 32 - prefix.getPrefixLength(); + routedIPCount += 1L << rank; + } + return routedIPCount; + } + + /** + * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set. + * + * This returns a BigInteger between 0 and 2**128. + * The behavior is undefined if any of the prefixes is not an IPv6 prefix or if the + * set is not ordered smallest prefix to longer prefix. + */ + public static BigInteger routedIPv6AddressCount(final TreeSet prefixes) { + BigInteger routedIPCount = BigInteger.ZERO; + for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) { + if (!prefix.isIPv6()) { + Log.wtf(TAG, "Non-IPv6 prefix in routedIPv6AddressCount"); + } + int rank = 128 - prefix.getPrefixLength(); + routedIPCount = routedIPCount.add(BigInteger.ONE.shiftLeft(rank)); + } + return routedIPCount; + } } diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java index fd465d95a9caffdea7198969fcf3a1232371044c..3164929943c4003ed4ef1136fc3aa33116eae2f9 100644 --- a/core/java/android/net/UidRange.java +++ b/core/java/android/net/UidRange.java @@ -21,8 +21,6 @@ import static android.os.UserHandle.PER_USER_RANGE; import android.os.Parcel; import android.os.Parcelable; -import java.lang.IllegalArgumentException; - /** * An inclusive range of UIDs. * @@ -52,6 +50,13 @@ public final class UidRange implements Parcelable { return start <= uid && uid <= stop; } + /** + * Returns the count of UIDs in this range. + */ + public int count() { + return 1 + stop - start; + } + /** * @return {@code true} if this range contains every UID contained by the {@param other} range. */ diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index a94b9284df3b48e53a4992a2f9ab06545ac7b601..f8a63ce5f1a3d8b0f3fa72d485e4ef7996dccd90 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -40,11 +40,12 @@ public final class IpManagerEvent implements Parcelable { public static final int ERROR_STARTING_IPV6 = 5; public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; public static final int ERROR_INVALID_PROVISIONING = 7; + public static final int ERROR_INTERFACE_NOT_FOUND = 8; @IntDef(value = { PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE, ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR, - ERROR_INVALID_PROVISIONING, + ERROR_INVALID_PROVISIONING, ERROR_INTERFACE_NOT_FOUND, }) @Retention(RetentionPolicy.SOURCE) public @interface EventType {} diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 8b4f02efd153ec3d6743618306bdc6947334942b..63631618856a56983f00707a8172727393b372a4 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.content.Intent; @@ -138,6 +139,23 @@ public class BatteryManager { */ public static final String EXTRA_SEQUENCE = "seq"; + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}: + * Contains list of Bundles representing battery events + * @hide + */ + @SystemApi + public static final String EXTRA_EVENTS = "android.os.extra.EVENTS"; + + /** + * Extra for event in {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}: + * Long value representing time when event occurred as returned by + * {@link android.os.SystemClock#elapsedRealtime()} + * @hide + */ + @SystemApi + public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP"; + // values for "status" field in the ACTION_BATTERY_CHANGED Intent public static final int BATTERY_STATUS_UNKNOWN = Constants.BATTERY_STATUS_UNKNOWN; public static final int BATTERY_STATUS_CHARGING = Constants.BATTERY_STATUS_CHARGING; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 5e9d39ce6af004fc9815ae47ca8e04a331c1226a..7bc5d5bd33bfd94289684f97a4fcc370d6155004 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -294,8 +294,8 @@ public abstract class BatteryStats implements Parcelable { /** * jbd line is: * BATTERY_STATS_CHECKIN_VERSION, uid, which, "jbd", - * jobsDeferredEventCount, jobsDeferredCount, averageLatencyMillis, - * count at latency < 1 hr, count at latency < 2 hrs, count at latency < 6 hrs, beyond 6 hrs + * jobsDeferredEventCount, jobsDeferredCount, totalLatencyMillis, + * count at latency < 1 hr, count at latency 1 to 2 hrs, 2 to 4 hrs, 4 to 8 hrs, and past 8 hrs *

    * @see #JOB_FRESHNESS_BUCKETS */ @@ -364,13 +364,14 @@ public abstract class BatteryStats implements Parcelable { /** * These are the thresholds for bucketing last time since a job was run for an app * that just moved to ACTIVE due to a launch. So if the last time a job ran was less - * than 30 minutes ago, then it's reasonably fresh, 2 hours ago, not so fresh and so + * than 1 hour ago, then it's reasonably fresh, 2 hours ago, not so fresh and so * on. */ public static final long[] JOB_FRESHNESS_BUCKETS = { 1 * 60 * 60 * 1000L, 2 * 60 * 60 * 1000L, - 6 * 60 * 60 * 1000L, + 4 * 60 * 60 * 1000L, + 8 * 60 * 60 * 1000L, Long.MAX_VALUE }; @@ -2269,27 +2270,27 @@ public abstract class BatteryStats implements Parcelable { */ public abstract int getMobileRadioActiveUnknownCount(int which); - public static final int DATA_CONNECTION_NONE = 0; - public static final int DATA_CONNECTION_GPRS = 1; - public static final int DATA_CONNECTION_EDGE = 2; - public static final int DATA_CONNECTION_UMTS = 3; - public static final int DATA_CONNECTION_CDMA = 4; - public static final int DATA_CONNECTION_EVDO_0 = 5; - public static final int DATA_CONNECTION_EVDO_A = 6; - public static final int DATA_CONNECTION_1xRTT = 7; - public static final int DATA_CONNECTION_HSDPA = 8; - public static final int DATA_CONNECTION_HSUPA = 9; - public static final int DATA_CONNECTION_HSPA = 10; - public static final int DATA_CONNECTION_IDEN = 11; - public static final int DATA_CONNECTION_EVDO_B = 12; - public static final int DATA_CONNECTION_LTE = 13; - public static final int DATA_CONNECTION_EHRPD = 14; - public static final int DATA_CONNECTION_HSPAP = 15; - public static final int DATA_CONNECTION_GSM = 16; - public static final int DATA_CONNECTION_TD_SCDMA = 17; - public static final int DATA_CONNECTION_IWLAN = 18; - public static final int DATA_CONNECTION_LTE_CA = 19; - public static final int DATA_CONNECTION_OTHER = 20; + public static final int DATA_CONNECTION_NONE = SystemProto.DataConnection.NONE; // 0 + public static final int DATA_CONNECTION_GPRS = SystemProto.DataConnection.GPRS; // 1 + public static final int DATA_CONNECTION_EDGE = SystemProto.DataConnection.EDGE; // 2 + public static final int DATA_CONNECTION_UMTS = SystemProto.DataConnection.UMTS; // 3 + public static final int DATA_CONNECTION_CDMA = SystemProto.DataConnection.CDMA; // 4 + public static final int DATA_CONNECTION_EVDO_0 = SystemProto.DataConnection.EVDO_0; // 5 + public static final int DATA_CONNECTION_EVDO_A = SystemProto.DataConnection.EVDO_A; // 6 + public static final int DATA_CONNECTION_1xRTT = SystemProto.DataConnection.ONE_X_RTT; // 7 + public static final int DATA_CONNECTION_HSDPA = SystemProto.DataConnection.HSDPA; // 8 + public static final int DATA_CONNECTION_HSUPA = SystemProto.DataConnection.HSUPA; // 9 + public static final int DATA_CONNECTION_HSPA = SystemProto.DataConnection.HSPA; // 10 + public static final int DATA_CONNECTION_IDEN = SystemProto.DataConnection.IDEN; // 11 + public static final int DATA_CONNECTION_EVDO_B = SystemProto.DataConnection.EVDO_B; // 12 + public static final int DATA_CONNECTION_LTE = SystemProto.DataConnection.LTE; // 13 + public static final int DATA_CONNECTION_EHRPD = SystemProto.DataConnection.EHRPD; // 14 + public static final int DATA_CONNECTION_HSPAP = SystemProto.DataConnection.HSPAP; // 15 + public static final int DATA_CONNECTION_GSM = SystemProto.DataConnection.GSM; // 16 + public static final int DATA_CONNECTION_TD_SCDMA = SystemProto.DataConnection.TD_SCDMA; // 17 + public static final int DATA_CONNECTION_IWLAN = SystemProto.DataConnection.IWLAN; // 18 + public static final int DATA_CONNECTION_LTE_CA = SystemProto.DataConnection.LTE_CA; // 19 + public static final int DATA_CONNECTION_OTHER = SystemProto.DataConnection.OTHER; // 20 static final String[] DATA_CONNECTION_NAMES = { "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 48f56847e88ddb141de1034ba45bbde158ae731c..df6ce8e85601b851db5902d5945a8d23a4c15e0e 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -19,6 +19,7 @@ package android.os; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Application; import android.content.Context; import android.text.TextUtils; @@ -287,6 +288,7 @@ public class Build { * we are operating under, we bump the assumed resource platform version by 1. * @hide */ + @TestApi public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length; /** @@ -894,6 +896,14 @@ public class Build { /** * P. + * + *

    Applications targeting this or a later release will get these + * new changes in behavior:

    + *
      + *
    • {@link android.app.Service#startForeground Service.startForeground} requires + * that apps hold the permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE}.
    • + *
    */ public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version. } diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java index eae7d7014bb1b0d20176eed3cc916474233ce68f..3d072c5c4f5e09f5945e68f35880d3faa2fa56a4 100644 --- a/core/java/android/os/HardwarePropertiesManager.java +++ b/core/java/android/os/HardwarePropertiesManager.java @@ -63,6 +63,8 @@ public class HardwarePropertiesManager { /** * Device temperature types. */ + // These constants are also defined in android/os/enums.proto. + // Any change to the types here or in the thermal hal should be made in the proto as well. /** Temperature of CPUs in Celsius. */ public static final int DEVICE_TEMPERATURE_CPU = Constants.TemperatureType.CPU; diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index eae52171ee4864e61049e33c6062159acd3f5f0b..29c298e31ae98686f80888990cd7ed9e39a14309 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -41,7 +41,7 @@ interface IStatsCompanionService { oneway void cancelAnomalyAlarm(); /** - * Register a repeating alarm for polling to fire at the given timestamp and every + * Register a repeating alarm for pulling to fire at the given timestamp and every * intervalMs thereafter (in ms since epoch). * If polling alarm had already been registered, it will be replaced by new one. * Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately, @@ -49,9 +49,19 @@ interface IStatsCompanionService { */ oneway void setPullingAlarms(long timestampMs, long intervalMs); - /** Cancel any repeating polling alarm. */ + /** Cancel any repeating pulling alarm. */ oneway void cancelPullingAlarms(); + /** + * Register an alarm when we want to trigger subscribers at the given + * timestamp (in ms since epoch). + * If an alarm had already been registered, it will be replaced by new one. + */ + oneway void setAlarmForSubscriberTriggering(long timestampMs); + + /** Cancel any alarm for the purpose of subscriber triggering. */ + oneway void cancelAlarmForSubscriberTriggering(); + /** Pull the specified data. Results will be sent to statsd when complete. */ StatsLogEventWrapper[] pullData(int pullCode); diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 682a24f17648b23dea9f8ce6cd0719ad57709625..2a68714431d403713eec8884cbb53c54c95c9267 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -46,6 +46,13 @@ interface IStatsManager { */ void informPollAlarmFired(); + /** + * Tells statsd that it is time to handle periodic alarms. Statsd will be responsible for + * determing what alarm subscriber to trigger. + * Two-way binder call so that caller's method (and corresponding wakelocks) will linger. + */ + void informAlarmForSubscriberTriggeringFired(); + /** * Tells statsd to store data to disk. */ diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 811cc5ed472c856514c3301649bc17b91fa2592f..97d415e18a733399fcbc5284e5c35d0f1d16bc39 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -538,6 +538,7 @@ public final class PowerManager { ServiceType.DATA_SAVER, ServiceType.FORCE_ALL_APPS_STANDBY, ServiceType.OPTIONAL_SENSORS, + ServiceType.AOD, }) public @interface ServiceType { int NULL = 0; @@ -551,6 +552,7 @@ public final class PowerManager { int SOUND = 8; int BATTERY_STATS = 9; int DATA_SAVER = 10; + int AOD = 14; /** * Whether to enable force-app-standby on all apps or not. diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 96e2ae3d5ac102845e5f867d6569332fc15006b5..21c1263c47d1d8eb02b25c287c3c28e1eeae4fab 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -599,7 +599,6 @@ public class Process { /** * Returns whether the current process is in an isolated sandbox. - * @hide */ public static final boolean isIsolated() { return isIsolated(myUid()); diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 76c13be3c1b2f4aaf598a36ea2c2c927431b3aaa..a93e25aa6d8934e1f5ae2381471b705360e636fd 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -479,6 +479,8 @@ public final class StrictMode { /** Initialize a Builder from an existing ThreadPolicy. */ public Builder(ThreadPolicy policy) { mMask = policy.mask; + mListener = policy.mListener; + mExecutor = policy.mCallbackExecutor; } /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 185620066454c518fbd9146f0f361e3948a8f5e4..a9eb3600ac5d1761c786c175467b850c42b242c7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -36,6 +36,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -691,14 +692,13 @@ public class UserManager { /** * Specifies that system error dialogs for crashed or unresponsive apps should not be shown. * In this case, the system will force-stop the app as if the user chooses the "close app" - * option on the UI. No feedback report will be collected as there is no way for the user to - * provide explicit consent. + * option on the UI. A feedback report isn't collected as there is no way for the user to + * provide explicit consent. The default value is false. * - * When this user restriction is set by device owners, it's applied to all users; when it's set - * by profile owners, it's only applied to the relevant profiles. - * The default value is false. + *

    When this user restriction is set by device owners, it's applied to all users. When set by + * the profile owner of the primary user or a secondary user, the restriction affects only the + * calling user. This user restriction has no effect on managed profiles. * - *

    This user restriction has no effect on managed profiles. *

    Key for user restrictions. *

    Type: Boolean * @see DevicePolicyManager#addUserRestriction(ComponentName, String) @@ -913,6 +913,9 @@ public class UserManager { * Specifies if user switching is blocked on the current user. * *

    This restriction can only be set by the device owner, it will be applied to all users. + * Device owner can still switch user via + * {@link DevicePolicyManager#switchUser(ComponentName, UserHandle)} when this restriction is + * set. * *

    The default value is false. * @@ -1039,6 +1042,85 @@ public class UserManager { */ public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1; + /** + * Indicates user operation is successful. + */ + public static final int USER_OPERATION_SUCCESS = 0; + + /** + * Indicates user operation failed for unknown reason. + */ + public static final int USER_OPERATION_ERROR_UNKNOWN = 1; + + /** + * Indicates user operation failed because target user is a managed profile. + */ + public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2; + + /** + * Indicates user operation failed because maximum running user limit has been reached. + */ + public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3; + + /** + * Indicates user operation failed because the target user is in the foreground. + */ + public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; + + /** + * Indicates user operation failed because device has low data storage. + */ + public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5; + + /** + * Indicates user operation failed because maximum user limit has been reached. + */ + public static final int USER_OPERATION_ERROR_MAX_USERS = 6; + + /** + * Result returned from various user operations. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "USER_OPERATION_" }, value = { + USER_OPERATION_SUCCESS, + USER_OPERATION_ERROR_UNKNOWN, + USER_OPERATION_ERROR_MANAGED_PROFILE, + USER_OPERATION_ERROR_MAX_RUNNING_USERS, + USER_OPERATION_ERROR_CURRENT_USER, + USER_OPERATION_ERROR_LOW_STORAGE, + USER_OPERATION_ERROR_MAX_USERS + }) + public @interface UserOperationResult {} + + /** + * Thrown to indicate user operation failed. + */ + public static class UserOperationException extends RuntimeException { + private final @UserOperationResult int mUserOperationResult; + + /** + * Constructs a UserOperationException with specific result code. + * + * @param message the detail message + * @param userOperationResult the result code + * @hide + */ + public UserOperationException(String message, + @UserOperationResult int userOperationResult) { + super(message); + mUserOperationResult = userOperationResult; + } + + /** + * Returns the operation result code. + */ + public @UserOperationResult int getUserOperationResult() { + return mUserOperationResult; + } + } + /** @hide */ public static UserManager get(Context context) { return (UserManager) context.getSystemService(Context.USER_SERVICE); @@ -2530,8 +2612,13 @@ public class UserManager { public static int getMaxSupportedUsers() { // Don't allow multiple users on certain builds if (android.os.Build.ID.startsWith("JVP")) return 1; - // Svelte devices don't get multi-user. - if (ActivityManager.isLowRamDeviceStatic()) return 1; + if (ActivityManager.isLowRamDeviceStatic()) { + // Low-ram devices are Svelte. Most of the time they don't get multi-user. + if ((Resources.getSystem().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) + != Configuration.UI_MODE_TYPE_TELEVISION) { + return 1; + } + } return SystemProperties.getInt("fw.max_users", Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers)); } diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index 6c9f1b25bf66a1f53b52d7fb5ac3f054284fd71c..1f6c3cc76ddd105136837436e86968133f273190 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -16,6 +16,7 @@ package android.os; import android.annotation.Nullable; +import android.content.Context; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -186,4 +187,38 @@ public abstract class UserManagerInternal { * @return the array of user ids. */ public abstract int[] getUserIds(); + + /** + * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group + * and that the {@code callingUserId} is not a managed profile and + * {@code targetUserId} is enabled. + * + * @return TRUE if the {@code callingUserId} can access {@code targetUserId}. FALSE + * otherwise + * + * @throws SecurityException if the calling user and {@code targetUser} are not in the same + * group and {@code throwSecurityException} is true, otherwise if will simply return false. + */ + public abstract boolean isProfileAccessible(int callingUserId, int targetUserId, + String debugMsg, boolean throwSecurityException); + + /** + * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return + * itself. + */ + public abstract int getProfileParentId(int userId); + + /** + * Checks whether changing a setting to a value is prohibited by the corresponding user + * restriction. + * + *

    See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction( + * Context, int, String, boolean)}, which should be in sync with this method. + * + * @return {@code true} if the change is prohibited, {@code false} if the change is allowed. + * + * @hide + */ + public abstract boolean isSettingRestrictedForUser(String setting, int userId, String value, + int callingUid); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 57418c8b98799988938fdf6b8d8d76ac0c21417b..ca4c796ab62dbd98000a711ce23a6a7fe2286548 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -493,11 +493,12 @@ public class ZygoteProcess { * Instructs the zygote to pre-load the classes and native libraries at the given paths * for the specified abi. Not all zygotes support this function. */ - public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey, - String abi) throws ZygoteStartFailedEx, IOException { + public boolean preloadPackageForAbi(String packagePath, String libsPath, String libFileName, + String cacheKey, String abi) throws ZygoteStartFailedEx, + IOException { synchronized(mLock) { ZygoteState state = openZygoteSocketIfNeeded(abi); - state.writer.write("4"); + state.writer.write("5"); state.writer.newLine(); state.writer.write("--preload-package"); @@ -509,6 +510,9 @@ public class ZygoteProcess { state.writer.write(libsPath); state.writer.newLine(); + state.writer.write(libFileName); + state.writer.newLine(); + state.writer.write(cacheKey); state.writer.newLine(); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 3b53260e5dd761cd111ac4bfa26a8a65b5b736b8..55a202fd3a66fc108ffce886081e9ca40f354ea1 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -185,7 +185,6 @@ interface IStorageManager { long getCacheSizeBytes(String volumeUuid, int uid) = 76; long getAllocatableBytes(String volumeUuid, int flags, String callingPackage) = 77; void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) = 78; - void secdiscard(in String path) = 79; - void runIdleMaintenance() = 80; - void abortIdleMaintenance() = 81; + void runIdleMaintenance() = 79; + void abortIdleMaintenance() = 80; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index f593e8056db95a05227d5d6df6140b5209d7907e..bf20e6a8296235d677a2b8a610de0c8678bfb2c9 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -50,7 +50,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemProperties; -import android.os.UserHandle; import android.provider.Settings; import android.system.ErrnoException; import android.system.Os; @@ -1322,15 +1321,6 @@ public class StorageManager { } } - /** {@hide} */ - public void secdiscard(String path) { - try { - mStorageManager.secdiscard(path); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** {@hide} */ public static boolean isUserKeyUnlocked(int userId) { if (sStorageManager == null) { diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index d77428193749178219e575dbbc6eef3024678f23..0ed252667533cbfbec4ce6c19027ca5e62258320 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -90,7 +90,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba private NotificationManager.Policy mNotificationPolicy; private boolean mAllowAlarms; - private boolean mAllowMediaSystem; + private boolean mAllowMedia; private boolean mAllowRinger; public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) { @@ -100,8 +100,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mNotificationPolicy = mNotificationManager.getNotificationPolicy(); mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy .PRIORITY_CATEGORY_ALARMS) != 0; - mAllowMediaSystem = (mNotificationPolicy.priorityCategories & NotificationManager.Policy - .PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0; + mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy + .PRIORITY_CATEGORY_MEDIA) != 0; mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted( mNotificationPolicy); mStreamType = streamType; @@ -139,8 +139,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba return stream == AudioManager.STREAM_ALARM; } - private static boolean isMediaOrSystemStream(int stream) { - return stream == AudioManager.STREAM_MUSIC || stream == AudioManager.STREAM_SYSTEM; + private static boolean isMediaStream(int stream) { + return stream == AudioManager.STREAM_MUSIC; } public void setSeekBar(SeekBar seekBar) { @@ -159,7 +159,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba || mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS && ((!mAllowAlarms && isAlarmsStream(mStreamType)) - || (!mAllowMediaSystem && isMediaOrSystemStream(mStreamType)) + || (!mAllowMedia && isMediaStream(mStreamType)) || (!mAllowRinger && isNotificationOrRing(mStreamType)))); } @@ -454,8 +454,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mNotificationPolicy = mNotificationManager.getNotificationPolicy(); mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy .PRIORITY_CATEGORY_ALARMS) != 0; - mAllowMediaSystem = (mNotificationPolicy.priorityCategories - & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0; + mAllowMedia = (mNotificationPolicy.priorityCategories + & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0; mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted( mNotificationPolicy); updateSlider(); diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 60df467bc20fc0a0063a3871324605609538fbb0..70de09ebe85f4033250a87c09bce2692b886e78b 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -223,14 +223,14 @@ public class CallLog { /** Call was WIFI call. */ public static final int FEATURES_WIFI = 1 << 3; - /** Call was on RTT at some point */ - public static final int FEATURES_RTT = 1 << 4; - /** * Indicates the call underwent Assisted Dialing. * @hide */ - public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10; + public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4; + + /** Call was on RTT at some point */ + public static final int FEATURES_RTT = 1 << 5; /** * The phone number as the user entered it. diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index d9808a3d141298e716681e0cdc53704f996939db..1da6602d6983154364f1e0cb19c82bb84cda158a 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -692,8 +692,8 @@ public final class MediaStore { // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo); // If the magic is non-zero, we simply return thumbnail if it does exist. // querying MediaProvider and simply return thumbnail. - MiniThumbFile thumbFile = new MiniThumbFile(isVideo ? Video.Media.EXTERNAL_CONTENT_URI - : Images.Media.EXTERNAL_CONTENT_URI); + MiniThumbFile thumbFile = MiniThumbFile.instance( + isVideo ? Video.Media.EXTERNAL_CONTENT_URI : Images.Media.EXTERNAL_CONTENT_URI); Cursor c = null; try { long magic = thumbFile.getMagic(origId); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index cb38c0fc55a3d9ec2f197b3d9d38c816f41dad76..fea9972c662cf87033ce4c97ca53015bc49e44a9 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -84,6 +84,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; import android.util.StatsLog; +import android.view.textservice.TextServicesManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.widget.ILockSettings; @@ -428,6 +429,20 @@ public final class Settings { public static final String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS"; + /** + * Activity Action: Show settings to allow configuration of data and view data usage. + *

    + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + *

    + * Input: Nothing. + *

    + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_DATA_USAGE_SETTINGS = + "android.settings.DATA_USAGE_SETTINGS"; + /** * Activity Action: Show settings to allow configuration of Bluetooth. *

    @@ -3131,7 +3146,7 @@ public final class Settings { public static final String ALWAYS_FINISH_ACTIVITIES = Global.ALWAYS_FINISH_ACTIVITIES; /** - * Determines which streams are affected by ringer mode changes. The + * Determines which streams are affected by ringer and zen mode changes. The * stream type's bit should be set to 1 if it should be muted when going * into an inaudible ringer mode. */ @@ -6169,10 +6184,13 @@ public final class Settings { /** * Integer property that specifies the type of color space adjustment to - * perform. Valid values are defined in AccessibilityManager: + * perform. Valid values are defined in AccessibilityManager and Settings arrays.xml: * - AccessibilityManager.DALTONIZER_DISABLED = -1 * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0 - * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12 + * - @string/daltonizer_mode_protanomaly = 11 + * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY and + * @string/daltonizer_mode_deuteranomaly = 12 + * - @string/daltonizer_mode_tritanomaly = 13 * * @hide */ @@ -6180,7 +6198,8 @@ public final class Settings { "accessibility_display_daltonizer"; private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR = - new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"}); + new SettingsValidators.DiscreteValueValidator( + new String[] {"-1", "0", "11", "12", "13"}); /** * Setting that specifies whether automatic click when the mouse pointer stops moving is @@ -7970,6 +7989,10 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); + if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) { + CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER); + CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE); + } } /** @hide */ @@ -8581,6 +8604,7 @@ public final class Settings { * (0 = false, 1 = true) * @hide */ + @SystemApi public static final String EUICC_PROVISIONED = "euicc_provisioned"; /** @@ -10515,8 +10539,13 @@ public final class Settings { * entity_list_default use ":" as delimiter for values. Ex: * *

    -         * smart_selection_dark_launch              (boolean)
    -         * smart_selection_enabled_for_edit_text    (boolean)
    +         * smart_linkify_enabled                    (boolean)
    +         * system_textclassifier_enabled            (boolean)
    +         * model_dark_launch_enabled                (boolean)
    +         * smart_selection_enabled                  (boolean)
    +         * smart_text_share_enabled                 (boolean)
    +         * smart_linkify_enabled                    (boolean)
    +         * smart_select_animation_enabled           (boolean)
              * suggest_selection_max_range_length       (int)
              * classify_text_max_range_length           (int)
              * generate_links_max_text_length           (int)
    @@ -10529,7 +10558,7 @@ public final class Settings {
              * 

    * Type: string * @hide - * see also android.view.textclassifier.TextClassifierConstants + * see also android.view.textclassifier.TextClassificationConstants */ public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants"; @@ -10571,6 +10600,21 @@ public final class Settings { */ public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled"; + /** + * Whether or not app auto restriction is enabled. When it is enabled, settings app will + * auto restrict the app if it has bad behavior(e.g. hold wakelock for long time). + * + * Type: boolean (0 for false, 1 for true) + * Default: 1 + * + * @hide + */ + public static final java.lang.String APP_AUTO_RESTRICTION_ENABLED = + "app_auto_restriction_enabled"; + + private static final Validator APP_AUTO_RESTRICTION_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Feature flag to enable or disable the Forced App Standby feature. * Type: int (0 for false, 1 for true) @@ -10588,6 +10632,32 @@ public final class Settings { public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED = "forced_app_standby_for_small_battery_enabled"; + /** + * Whether or not to enable the Off Body, Radios Off feature on small battery devices. + * Type: int (0 for false, 1 for true) + * Default: 0 + * @hide + */ + public static final String OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED + = "off_body_radios_off_for_small_battery_enabled"; + + /** + * How long after the device goes off body to disable radios, in milliseconds. + * Type: long + * Default: 10 minutes + * @hide + */ + public static final String OFF_BODY_RADIOS_OFF_DELAY_MS = "off_body_radios_off_delay_ms"; + + /** + * Whether or not to turn on Wifi when proxy is disconnected. + * Type: int (0 for false, 1 for true) + * Default: 1 + * @hide + */ + public static final String WIFI_ON_WHEN_PROXY_DISCONNECTED + = "wifi_on_when_proxy_disconnected"; + /** * Whether or not to enable Time Only Mode for watch type devices. * Type: int (0 for false, 1 for true) @@ -10845,6 +10915,15 @@ public final class Settings { private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR = new SettingsValidators.InclusiveIntegerRangeValidator(0, 100); + + /** + * The max value for {@link #LOW_POWER_MODE_TRIGGER_LEVEL}. If this setting is not set + * or the value is 0, the default max will be used. + * + * @hide + */ + public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max"; + /** * If not 0, the activity manager will aggressively finish activities and * processes as soon as they are no longer needed. If 0, the normal @@ -11445,6 +11524,7 @@ public final class Settings { * * @hide */ + @TestApi public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; @@ -11467,6 +11547,7 @@ public final class Settings { public static final String[] SETTINGS_TO_BACKUP = { BUGREPORT_IN_POWER_MENU, STAY_ON_WHILE_PLUGGED_IN, + APP_AUTO_RESTRICTION_ENABLED, AUTO_TIME, AUTO_TIME_ZONE, POWER_SOUNDS_ENABLED, @@ -11519,12 +11600,15 @@ public final class Settings { VALIDATORS.put(DOCK_AUDIO_MEDIA_ENABLED, DOCK_AUDIO_MEDIA_ENABLED_VALIDATOR); VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR); VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR); + VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL_MAX, + LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR); VALIDATORS.put(BLUETOOTH_ON, BLUETOOTH_ON_VALIDATOR); VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR); VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR); VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR); VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON, WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR); + VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR); } /** diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationDialog.java index e9df3705db6ef4eb17ad8383d1cd17f2f71729ee..f6127e184139789f5df3d928efe50cdbcf9c6325 100644 --- a/core/java/android/security/ConfirmationDialog.java +++ b/core/java/android/security/ConfirmationDialog.java @@ -17,7 +17,10 @@ package android.security; import android.annotation.NonNull; +import android.content.ContentResolver; import android.content.Context; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; @@ -86,6 +89,7 @@ public class ConfirmationDialog { private byte[] mExtraData; private ConfirmationCallback mCallback; private Executor mExecutor; + private Context mContext; private final KeyStore mKeyStore = KeyStore.getInstance(); @@ -190,15 +194,39 @@ public class ConfirmationDialog { if (mExtraData == null) { throw new IllegalArgumentException("extraData must be set"); } - return new ConfirmationDialog(mPromptText, mExtraData); + return new ConfirmationDialog(context, mPromptText, mExtraData); } } - private ConfirmationDialog(CharSequence promptText, byte[] extraData) { + private ConfirmationDialog(Context context, CharSequence promptText, byte[] extraData) { + mContext = context; mPromptText = promptText; mExtraData = extraData; } + private static final int UI_OPTION_ACCESSIBILITY_INVERTED_FLAG = 1 << 0; + private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1; + + private int getUiOptionsAsFlags() { + int uiOptionsAsFlags = 0; + try { + ContentResolver contentResolver = mContext.getContentResolver(); + int inversionEnabled = Settings.Secure.getInt(contentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + if (inversionEnabled == 1) { + uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG; + } + float fontScale = Settings.System.getFloat(contentResolver, + Settings.System.FONT_SCALE); + if (fontScale > 1.0) { + uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG; + } + } catch (SettingNotFoundException e) { + Log.w(TAG, "Unexpected SettingNotFoundException"); + } + return uiOptionsAsFlags; + } + /** * Requests a confirmation prompt to be presented to the user. * @@ -220,8 +248,7 @@ public class ConfirmationDialog { mCallback = callback; mExecutor = executor; - int uiOptionsAsFlags = 0; - // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed. + int uiOptionsAsFlags = getUiOptionsAsFlags(); String locale = Locale.getDefault().toLanguageTag(); int responseCode = mKeyStore.presentConfirmationPrompt( mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags); @@ -277,7 +304,6 @@ public class ConfirmationDialog { * @return true if confirmation prompts are supported by the device. */ public static boolean isSupported() { - // TODO: read and return system property. - return true; + return KeyStore.getInstance().isConfirmationPromptSupported(); } } diff --git a/core/java/android/security/keystore/BadCertificateFormatException.java b/core/java/android/security/keystore/BadCertificateFormatException.java index ddc7bd2366acf7b07c5270b5e6ac7b47f2cb512c..c51b7737e823d3a82d1fe0ee058a1f56a9814af6 100644 --- a/core/java/android/security/keystore/BadCertificateFormatException.java +++ b/core/java/android/security/keystore/BadCertificateFormatException.java @@ -17,8 +17,7 @@ package android.security.keystore; /** - * Error thrown when the recovery agent supplies an invalid X509 certificate. - * + * @deprecated Use {@link android.security.keystore.recovery.BadCertificateFormatException}. * @hide */ public class BadCertificateFormatException extends RecoveryControllerException { diff --git a/core/java/android/security/keystore/DecryptionFailedException.java b/core/java/android/security/keystore/DecryptionFailedException.java index 945fcf6f88f203b69493376cd00130ead853dbb4..c0b52f714d0b0ebedfb3bdb39013f9e1b44bae93 100644 --- a/core/java/android/security/keystore/DecryptionFailedException.java +++ b/core/java/android/security/keystore/DecryptionFailedException.java @@ -17,9 +17,7 @@ package android.security.keystore; /** - * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key, - * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc. - * + * @deprecated Use {@link android.security.keystore.recovery.DecryptionFailedException}. * @hide */ public class DecryptionFailedException extends RecoveryControllerException { diff --git a/core/java/android/security/keystore/InternalRecoveryServiceException.java b/core/java/android/security/keystore/InternalRecoveryServiceException.java index 85829bed919134620c79016ccc02177035a69bf9..40076f732b9817eafe8d0be8d59bf9f2b733b84c 100644 --- a/core/java/android/security/keystore/InternalRecoveryServiceException.java +++ b/core/java/android/security/keystore/InternalRecoveryServiceException.java @@ -17,11 +17,7 @@ package android.security.keystore; /** - * An error thrown when something went wrong internally in the recovery service. - * - *

    This is an unexpected error, and indicates a problem with the service itself, rather than the - * caller having performed some kind of illegal action. - * + * @deprecated Use {@link android.security.keystore.recovery.InternalRecoveryServiceException}. * @hide */ public class InternalRecoveryServiceException extends RecoveryControllerException { diff --git a/core/java/android/security/keystore/KeyDerivationParams.java b/core/java/android/security/keystore/KeyDerivationParams.java index b19cee2d31a4b6419d389219ec5848b33ebc5074..e475dc36e1c34bef283ffcef5f98e3d1ed64c891 100644 --- a/core/java/android/security/keystore/KeyDerivationParams.java +++ b/core/java/android/security/keystore/KeyDerivationParams.java @@ -27,9 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Collection of parameters which define a key derivation function. - * Currently only supports salted SHA-256 - * + * @deprecated Use {@link android.security.keystore.recovery.KeyDerivationParams}. * @hide */ public final class KeyDerivationParams implements Parcelable { diff --git a/core/java/android/security/keystore/KeychainProtectionParams.java b/core/java/android/security/keystore/KeychainProtectionParams.java index a940fdc778a9307719fcc7c64930c9d4a2b9e400..19a087d5d1d4294a395cddfd4c7bd2ee1693c3ab 100644 --- a/core/java/android/security/keystore/KeychainProtectionParams.java +++ b/core/java/android/security/keystore/KeychainProtectionParams.java @@ -28,23 +28,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** - * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This - * class wraps all the data necessary to derive the same key on a recovering device: - * - *

      - *
    • UI parameters for the user's lock screen - so that if e.g., the user was using a pattern, - * the recovering device can display the pattern UI to the user when asking them to enter - * the lock screen from their previous device. - *
    • The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt. - *
    - * - *

    As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current - * version of the keychain. - * - *

    For now, the recoverable keychain only supports a single layer of protection, which is the - * user's lock screen. In the future, the keychain will support multiple layers of protection - * (e.g. an additional keychain password, along with the lock screen). - * + * @deprecated Use {@link android.security.keystore.recovery.KeyChainProtectionParams}. * @hide */ public final class KeychainProtectionParams implements Parcelable { diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java index 23aec25eb12851b63b125b9974b9b24aa6610403..cf18fd1c6a0b87e2e1903dce476c9e3d8b5d5041 100644 --- a/core/java/android/security/keystore/KeychainSnapshot.java +++ b/core/java/android/security/keystore/KeychainSnapshot.java @@ -25,21 +25,7 @@ import com.android.internal.util.Preconditions; import java.util.List; /** - * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot: - * - *

      - *
    • The user's lock screen changes. (A key derived from the user's lock screen is used to - * protected the keychain, which is why this forces a new snapshot.) - *
    • A key is added to or removed from the recoverable keychain. - *
    - * - *

    The snapshot data is also encrypted with the remote trusted hardware's public key, so even - * the recovery agent itself should not be able to decipher the data. The recovery agent sends an - * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a - * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then - * sends it to the framework, where it is decrypted using the user's lock screen from their previous - * device. - * + * @deprecated Use {@link android.security.keystore.recovery.KeyChainSnapshot}. * @hide */ public final class KeychainSnapshot implements Parcelable { diff --git a/core/java/android/security/keystore/LockScreenRequiredException.java b/core/java/android/security/keystore/LockScreenRequiredException.java index b07fb9cdd002e4122d04ac925b4558eb46ee5db5..097028457c9e6ba6890f40b8a2d3cc0e78e919c2 100644 --- a/core/java/android/security/keystore/LockScreenRequiredException.java +++ b/core/java/android/security/keystore/LockScreenRequiredException.java @@ -17,10 +17,7 @@ package android.security.keystore; /** - * Error thrown when trying to generate keys for a profile that has no lock screen set. - * - *

    A lock screen must be set, as the lock screen is used to encrypt the snapshot. - * + * @deprecated Use {@link android.security.keystore.recovery.LockScreenRequiredException}. * @hide */ public class LockScreenRequiredException extends RecoveryControllerException { diff --git a/core/java/android/security/keystore/RecoveryClaim.java b/core/java/android/security/keystore/RecoveryClaim.java index 6f566af1dc7df9d10156ab8fa3c5ad52ab54cabe..12be607a23d480aa098a8786ee5ebda42ab009e2 100644 --- a/core/java/android/security/keystore/RecoveryClaim.java +++ b/core/java/android/security/keystore/RecoveryClaim.java @@ -17,8 +17,7 @@ package android.security.keystore; /** - * An attempt to recover a keychain protected by remote secure hardware. - * + * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}. * @hide */ public class RecoveryClaim { diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java index 4a0de5f2c7f0dc3e1cf43f3110e56ce72b02f10d..145261e3b71d02222f0213cb818a2335ad5d3bbe 100644 --- a/core/java/android/security/keystore/RecoveryController.java +++ b/core/java/android/security/keystore/RecoveryController.java @@ -31,22 +31,6 @@ import java.util.List; import java.util.Map; /** - * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by - * other Android devices belonging to the user. The exported keychain is protected by the user's - * lock screen. - * - *

    The RecoveryController must be paired with a recovery agent. The recovery agent is responsible - * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force - * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10). - * After that number of incorrect guesses, the trusted hardware no longer allows access to the - * key chain. - * - *

    For now only the recovery agent itself is able to create keys, so it is expected that the - * recovery agent is itself the system app. - * - *

    A recovery agent requires the privileged permission - * {@code android.Manifest.permission#RECOVER_KEYSTORE}. - * * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}. * @hide */ diff --git a/core/java/android/security/keystore/RecoveryControllerException.java b/core/java/android/security/keystore/RecoveryControllerException.java index 5b806b75ebabfb8a522d52ab10315c15bf0c60db..f990c236c9d3b8dea096871c7f1150decb5b72e4 100644 --- a/core/java/android/security/keystore/RecoveryControllerException.java +++ b/core/java/android/security/keystore/RecoveryControllerException.java @@ -19,8 +19,7 @@ package android.security.keystore; import java.security.GeneralSecurityException; /** - * Base exception for errors thrown by {@link RecoveryController}. - * + * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}. * @hide */ public abstract class RecoveryControllerException extends GeneralSecurityException { diff --git a/core/java/android/security/keystore/RecoverySession.java b/core/java/android/security/keystore/RecoverySession.java index ae8d91af3230fe4726db0e443ca191f5e3eb5edf..8a3e06b7deb1942fea1146774157a2ceac0e79ea 100644 --- a/core/java/android/security/keystore/RecoverySession.java +++ b/core/java/android/security/keystore/RecoverySession.java @@ -19,9 +19,7 @@ package android.security.keystore; import java.security.SecureRandom; /** - * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a - * recovery agent. - * + * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}. * @hide */ public class RecoverySession implements AutoCloseable { diff --git a/core/java/android/security/keystore/SessionExpiredException.java b/core/java/android/security/keystore/SessionExpiredException.java index f13e2060262565ece4528a20516e2aefb781925d..7c8d5e4f52f950224b11344aa09948570febab25 100644 --- a/core/java/android/security/keystore/SessionExpiredException.java +++ b/core/java/android/security/keystore/SessionExpiredException.java @@ -17,8 +17,7 @@ package android.security.keystore; /** - * Error thrown when attempting to use a {@link RecoverySession} that has since expired. - * + * @deprecated Use {@link android.security.keystore.recovery.SessionExpiredException}. * @hide */ public class SessionExpiredException extends RecoveryControllerException { diff --git a/core/java/android/security/keystore/WrappedApplicationKey.java b/core/java/android/security/keystore/WrappedApplicationKey.java index 522bb9557b8d738af444708afde56abde2dc6412..2ce8c7d395d5c67fb7e3d00ddc0b203d81e44340 100644 --- a/core/java/android/security/keystore/WrappedApplicationKey.java +++ b/core/java/android/security/keystore/WrappedApplicationKey.java @@ -23,16 +23,7 @@ import android.os.Parcelable; import com.android.internal.util.Preconditions; /** - * Helper class with data necessary recover a single application key, given a recovery key. - * - *

      - *
    • Alias - Keystore alias of the key. - *
    • Encrypted key material. - *
    - * - * Note that Application info is not included. Recovery Agent can only make its own keys - * recoverable. - * + * @deprecated Use {@link android.security.keystore.recovery.WrappedApplicationKey}. * @hide */ public final class WrappedApplicationKey implements Parcelable { diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java index f043d6a11fb8fde313f765b9b0e113d1800ac255..4580c47ac3c3a7bc59764f8b53c2824dd1c4819b 100644 --- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java +++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java @@ -116,7 +116,6 @@ public final class KeyChainSnapshot implements Parcelable { * See implementation for binary key format. * * @deprecated Use {@link #getTrustedHardwareCertPath} instead. - * @removed */ @Deprecated public @NonNull byte[] getTrustedHardwarePublicKey() { @@ -221,7 +220,6 @@ public final class KeyChainSnapshot implements Parcelable { * @param publicKey The public key * @return This builder. * @deprecated Use {@link #setTrustedHardwareCertPath} instead. - * @removed */ @Deprecated public Builder setTrustedHardwarePublicKey(byte[] publicKey) { diff --git a/core/java/android/security/keystore/recovery/RecoveryClaim.java b/core/java/android/security/keystore/recovery/RecoveryClaim.java deleted file mode 100644 index 45c6b4ff6758c29e305adba401b72430d3d0867f..0000000000000000000000000000000000000000 --- a/core/java/android/security/keystore/recovery/RecoveryClaim.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.security.keystore.recovery; - -/** - * An attempt to recover a keychain protected by remote secure hardware. - * - * @hide - * Deprecated - */ -public class RecoveryClaim { - - private final RecoverySession mRecoverySession; - private final byte[] mClaimBytes; - - RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) { - mRecoverySession = recoverySession; - mClaimBytes = claimBytes; - } - - /** - * Returns the session associated with the recovery attempt. This is used to match the symmetric - * key, which remains internal to the framework, for decrypting the claim response. - * - * @return The session data. - */ - public RecoverySession getRecoverySession() { - return mRecoverySession; - } - - /** - * Returns the encrypted claim's bytes. - * - *

    This should be sent by the recovery agent to the remote secure hardware, which will use - * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key - * to the device. - */ - public byte[] getClaimBytes() { - return mClaimBytes; - } -} diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index 0683e02b66a7ff21994a812b5d1a081051fc2023..48813757aaa895067adcc31e94cf600b0af61e40 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -99,7 +99,11 @@ public class RecoveryController { public static final int ERROR_SESSION_EXPIRED = 24; /** - * Failed because the provided certificate was not a valid X509 certificate. + * Failed because the format of the provided certificate is incorrect, e.g., cannot be decoded + * properly or misses necessary fields. + * + *

    Note that this is different from {@link #ERROR_INVALID_CERTIFICATE}, which implies the + * certificate has a correct format but cannot be validated. * * @hide */ @@ -113,6 +117,24 @@ public class RecoveryController { */ public static final int ERROR_DECRYPTION_FAILED = 26; + /** + * Error thrown if the format of a given key is invalid. This might be because the key has a + * wrong length, invalid content, etc. + * + * @hide + */ + public static final int ERROR_INVALID_KEY_FORMAT = 27; + + /** + * Failed because the provided certificate cannot be validated, e.g., is expired or has invalid + * signatures. + * + *

    Note that this is different from {@link #ERROR_BAD_CERTIFICATE_FORMAT}, which denotes + * incorrect certificate formats, e.g., due to wrong encoding or structure. + * + * @hide + */ + public static final int ERROR_INVALID_CERTIFICATE = 28; private final ILockSettings mBinder; private final KeyStore mKeyStore; @@ -141,33 +163,66 @@ public class RecoveryController { } /** - * Initializes key recovery service for the calling application. RecoveryController - * randomly chooses one of the keys from the list and keeps it to use for future key export - * operations. Collection of all keys in the list must be signed by the provided {@code - * rootCertificateAlias}, which must also be present in the list of root certificates - * preinstalled on the device. The random selection allows RecoveryController to select - * which of a set of remote recovery service devices will be used. - * - *

    In addition, RecoveryController enforces a delay of three months between - * consecutive initialization attempts, to limit the ability of an attacker to often switch - * remote recovery devices and significantly increase number of recovery attempts. + * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) + public void initRecoveryService( + @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList) + throws CertificateException, InternalRecoveryServiceException { + try { + mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT + || e.errorCode == ERROR_INVALID_CERTIFICATE) { + throw new CertificateException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); + } + } + + /** + * Initializes the recovery service for the calling application. The detailed steps should be: + *

      + *
    1. Parse {@code signatureFile} to get relevant information. + *
    2. Validate the signer's X509 certificate, contained in {@code signatureFile}, against + * the root certificate pre-installed in the OS and chosen by {@code + * rootCertificateAlias}. + *
    3. Verify the public-key signature, contained in {@code signatureFile}, and verify it + * against the entire {@code certificateFile}. + *
    4. Parse {@code certificateFile} to get relevant information. + *
    5. Check the serial number, contained in {@code certificateFile}, and skip the following + * steps if the serial number is not larger than the one previously stored. + *
    6. Randomly choose a X509 certificate from the endpoint X509 certificates, contained in + * {@code certificateFile}, and validate it against the root certificate pre-installed + * in the OS and chosen by {@code rootCertificateAlias}. + *
    7. Store the chosen X509 certificate and the serial in local database for later use. + *
    * - * @param rootCertificateAlias alias of a root certificate preinstalled on the device - * @param signedPublicKeyList binary blob a list of X509 certificates and signature - * @throws CertificateException if the {@code signedPublicKeyList} is in a bad format. + * @param rootCertificateAlias the alias of a root certificate pre-installed in the OS + * @param certificateFile the binary content of the XML file containing a list of recovery + * service X509 certificates, and other metadata including the serial number + * @param signatureFile the binary content of the XML file containing the public-key signature + * of the entire certificate file, and a signer's X509 certificate + * @throws CertificateException if the given certificate files cannot be parsed or validated * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery * service. */ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) public void initRecoveryService( - @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList) + @NonNull String rootCertificateAlias, @NonNull byte[] certificateFile, + @NonNull byte[] signatureFile) throws CertificateException, InternalRecoveryServiceException { try { - mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList); + mBinder.initRecoveryServiceWithSigFile( + rootCertificateAlias, certificateFile, signatureFile); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) { + if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT + || e.errorCode == ERROR_INVALID_CERTIFICATE) { throw new CertificateException(e.getMessage()); } throw wrapUnexpectedServiceSpecificException(e); @@ -176,7 +231,6 @@ public class RecoveryController { /** * @deprecated Use {@link #getKeyChainSnapshot()} - * @removed */ @Deprecated @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) @@ -254,7 +308,6 @@ public class RecoveryController { /** * @deprecated Use {@link #getAliases()}. - * @removed */ @Deprecated public List getAliases(@Nullable String packageName) @@ -278,7 +331,6 @@ public class RecoveryController { /** * @deprecated Use {@link #setRecoveryStatus(String, int)} - * @removed */ @Deprecated @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) @@ -313,7 +365,6 @@ public class RecoveryController { /** * @deprecated Use {@link #getRecoveryStatus(String)}. - * @removed */ @Deprecated public int getRecoveryStatus(String packageName, String alias) @@ -461,9 +512,9 @@ public class RecoveryController { } } + // TODO: Unhide the following APIs, generateKey(), importKey(), and getKey() /** * @deprecated Use {@link #generateKey(String)}. - * @removed */ @Deprecated public Key generateKey(@NonNull String alias, byte[] account) @@ -502,6 +553,40 @@ public class RecoveryController { } } + /** + * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code + * keyBytes}. + * + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock + * screen is required to generate recoverable keys. + * + * @hide + */ + public Key importKey(@NonNull String alias, byte[] keyBytes) + throws InternalRecoveryServiceException, LockScreenRequiredException { + try { + String grantAlias = mBinder.importKey(alias, keyBytes); + if (grantAlias == null) { + throw new InternalRecoveryServiceException("Null grant alias"); + } + return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( + mKeyStore, + grantAlias, + KeyStore.UID_SELF); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (UnrecoverableKeyException e) { + throw new InternalRecoveryServiceException("Failed to get key from keystore", e); + } catch (ServiceSpecificException e) { + if (e.errorCode == ERROR_INSECURE_USER) { + throw new LockScreenRequiredException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); + } + } + /** * Gets a key called {@code alias} from the recoverable key store. * diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java index e42c7666ee10e381517d6978467f1764332290ca..137dd8946c9a58c80d45ab15eba32db19b50a69e 100644 --- a/core/java/android/security/keystore/recovery/RecoverySession.java +++ b/core/java/android/security/keystore/recovery/RecoverySession.java @@ -73,7 +73,6 @@ public class RecoverySession implements AutoCloseable { /** * @deprecated Use {@link #start(CertPath, byte[], byte[], List)} instead. - * @removed */ @Deprecated @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) @@ -95,7 +94,8 @@ public class RecoverySession implements AutoCloseable { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) { + if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT + || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { throw new CertificateException(e.getMessage()); } throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); @@ -144,7 +144,8 @@ public class RecoverySession implements AutoCloseable { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) { + if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT + || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { throw new CertificateException(e.getMessage()); } throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java index df9766d8484321755d080952e4ce4fb033b5bd89..714e35a2e6fcab855ffc15226618b95590851aed 100644 --- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java +++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java @@ -61,7 +61,6 @@ public final class WrappedApplicationKey implements Parcelable { /** * @deprecated AOSP does not associate keys with accounts. This may be done by system app. - * @removed */ @Deprecated public Builder setAccount(@NonNull byte[] account) { @@ -120,7 +119,6 @@ public final class WrappedApplicationKey implements Parcelable { /** * @deprecated AOSP does not associate keys with accounts. This may be done by system app. - * @removed */ @Deprecated public @NonNull byte[] getAccount() { diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java index cb4f10455ec968eef1ca87ed996081062294d500..4bbee616dca03c31a101ff33526002ea0ebe2ef0 100644 --- a/core/java/android/service/euicc/EuiccProfileInfo.java +++ b/core/java/android/service/euicc/EuiccProfileInfo.java @@ -441,7 +441,7 @@ public final class EuiccProfileInfo implements Parcelable { + ", state=" + mState + ", CarrierIdentifier=" - + mCarrierIdentifier.toString() + + mCarrierIdentifier + ", policyRules=" + mPolicyRules + ", accessRules=" diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 422e36baee70adf255b4ccc574c204170489b461..ea10ae7b380808904b1e9e5ac1a1f80f107d8fa6 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -85,7 +85,10 @@ import java.util.List; * or after {@link #onListenerDisconnected()}. *

    *

    Notification listeners cannot get notification access or be bound by the system on - * {@link ActivityManager#isLowRamDevice() low ram} devices

    + * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices. The system also ignores + * notification listeners running in a work profile. A + * {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work + * profile.

    */ public abstract class NotificationListenerService extends Service { @@ -146,13 +149,19 @@ public abstract class NotificationListenerService extends Service { /** * Whether notification suppressed by DND should not interruption visually when the screen is * off. + * + * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_OFF = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; /** * Whether notification suppressed by DND should not interruption visually when the screen is * on. + * + * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}. */ + @Deprecated public static final int SUPPRESSED_EFFECT_SCREEN_ON = NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; @@ -1217,6 +1226,7 @@ public abstract class NotificationListenerService extends Service { // convert icon metadata to legacy format for older clients createLegacyIconExtras(sbn.getNotification()); maybePopulateRemoteViews(sbn.getNotification()); + maybePopulatePeople(sbn.getNotification()); } catch (IllegalArgumentException e) { // warn and drop corrupt notification Log.w(TAG, "onNotificationPosted: can't rebuild notification from " + @@ -1449,7 +1459,8 @@ public abstract class NotificationListenerService extends Service { /** * Returns the type(s) of visual effects that should be suppressed for this notification. - * See {@link #SUPPRESSED_EFFECT_SCREEN_OFF}, {@link #SUPPRESSED_EFFECT_SCREEN_ON}. + * See {@link NotificationManager.Policy}, e.g. + * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}. */ public int getSuppressedVisualEffects() { return mSuppressedVisualEffects; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 171d4d938beb33cdf6556a22b05a30d48fc60903..740a387e078d78c954f12dacad7d0c006142c890 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -85,22 +85,26 @@ public class ZenModeConfig implements Parcelable { // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values: private static final boolean DEFAULT_ALLOW_ALARMS = true; - private static final boolean DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER = true; + private static final boolean DEFAULT_ALLOW_MEDIA = true; + private static final boolean DEFAULT_ALLOW_SYSTEM = false; private static final boolean DEFAULT_ALLOW_CALLS = false; private static final boolean DEFAULT_ALLOW_MESSAGES = false; private static final boolean DEFAULT_ALLOW_REMINDERS = false; private static final boolean DEFAULT_ALLOW_EVENTS = false; private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false; - private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true; - private static final boolean DEFAULT_ALLOW_SCREEN_ON = true; + private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false; + private static final boolean DEFAULT_ALLOW_SCREEN_ON = false; + private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = + Policy.getAllSuppressedVisualEffects(); - public static final int XML_VERSION = 3; + public static final int XML_VERSION = 5; public static final String ZEN_TAG = "zen"; private static final String ZEN_ATT_VERSION = "version"; private static final String ZEN_ATT_USER = "user"; private static final String ALLOW_TAG = "allow"; private static final String ALLOW_ATT_ALARMS = "alarms"; - private static final String ALLOW_ATT_MEDIA_SYSTEM_OTHER = "media_system_other"; + private static final String ALLOW_ATT_MEDIA = "media"; + private static final String ALLOW_ATT_SYSTEM = "system"; private static final String ALLOW_ATT_CALLS = "calls"; private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers"; private static final String ALLOW_ATT_MESSAGES = "messages"; @@ -111,6 +115,8 @@ public class ZenModeConfig implements Parcelable { private static final String ALLOW_ATT_EVENTS = "events"; private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff"; private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn"; + private static final String DISALLOW_TAG = "disallow"; + private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects"; private static final String CONDITION_ATT_ID = "id"; private static final String CONDITION_ATT_SUMMARY = "summary"; @@ -134,7 +140,8 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ENABLER = "enabler"; public boolean allowAlarms = DEFAULT_ALLOW_ALARMS; - public boolean allowMediaSystemOther = DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER; + public boolean allowMedia = DEFAULT_ALLOW_MEDIA; + public boolean allowSystem = DEFAULT_ALLOW_SYSTEM; public boolean allowCalls = DEFAULT_ALLOW_CALLS; public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS; public boolean allowMessages = DEFAULT_ALLOW_MESSAGES; @@ -143,6 +150,7 @@ public class ZenModeConfig implements Parcelable { public int allowCallsFrom = DEFAULT_SOURCE; public int allowMessagesFrom = DEFAULT_SOURCE; public int user = UserHandle.USER_SYSTEM; + public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS; public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF; public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON; public int version; @@ -175,7 +183,9 @@ public class ZenModeConfig implements Parcelable { allowWhenScreenOff = source.readInt() == 1; allowWhenScreenOn = source.readInt() == 1; allowAlarms = source.readInt() == 1; - allowMediaSystemOther = source.readInt() == 1; + allowMedia = source.readInt() == 1; + allowSystem = source.readInt() == 1; + suppressedVisualEffects = source.readInt(); } @Override @@ -206,7 +216,9 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(allowWhenScreenOff ? 1 : 0); dest.writeInt(allowWhenScreenOn ? 1 : 0); dest.writeInt(allowAlarms ? 1 : 0); - dest.writeInt(allowMediaSystemOther ? 1 : 0); + dest.writeInt(allowMedia ? 1 : 0); + dest.writeInt(allowSystem ? 1 : 0); + dest.writeInt(suppressedVisualEffects); } @Override @@ -214,7 +226,8 @@ public class ZenModeConfig implements Parcelable { return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') .append("user=").append(user) .append(",allowAlarms=").append(allowAlarms) - .append(",allowMediaSystemOther=").append(allowMediaSystemOther) + .append(",allowMedia=").append(allowMedia) + .append(",allowSystem=").append(allowSystem) .append(",allowReminders=").append(allowReminders) .append(",allowEvents=").append(allowEvents) .append(",allowCalls=").append(allowCalls) @@ -224,6 +237,7 @@ public class ZenModeConfig implements Parcelable { .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) .append(",allowWhenScreenOff=").append(allowWhenScreenOff) .append(",allowWhenScreenOn=").append(allowWhenScreenOn) + .append(",suppressedVisualEffects=").append(suppressedVisualEffects) .append(",automaticRules=").append(automaticRules) .append(",manualRule=").append(manualRule) .append(']').toString(); @@ -240,8 +254,11 @@ public class ZenModeConfig implements Parcelable { if (allowAlarms != to.allowAlarms) { d.addLine("allowAlarms", allowAlarms, to.allowAlarms); } - if (allowMediaSystemOther != to.allowMediaSystemOther) { - d.addLine("allowMediaSystemOther", allowMediaSystemOther, to.allowMediaSystemOther); + if (allowMedia != to.allowMedia) { + d.addLine("allowMedia", allowMedia, to.allowMedia); + } + if (allowSystem != to.allowSystem) { + d.addLine("allowSystem", allowSystem, to.allowSystem); } if (allowCalls != to.allowCalls) { d.addLine("allowCalls", allowCalls, to.allowCalls); @@ -270,6 +287,10 @@ public class ZenModeConfig implements Parcelable { if (allowWhenScreenOn != to.allowWhenScreenOn) { d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn); } + if (suppressedVisualEffects != to.suppressedVisualEffects) { + d.addLine("suppressedVisualEffects", suppressedVisualEffects, + to.suppressedVisualEffects); + } final ArraySet allRules = new ArraySet<>(); addKeys(allRules, automaticRules); addKeys(allRules, to.automaticRules); @@ -361,7 +382,8 @@ public class ZenModeConfig implements Parcelable { if (o == this) return true; final ZenModeConfig other = (ZenModeConfig) o; return other.allowAlarms == allowAlarms - && other.allowMediaSystemOther == allowMediaSystemOther + && other.allowMedia == allowMedia + && other.allowSystem == allowSystem && other.allowCalls == allowCalls && other.allowRepeatCallers == allowRepeatCallers && other.allowMessages == allowMessages @@ -373,15 +395,17 @@ public class ZenModeConfig implements Parcelable { && other.allowWhenScreenOn == allowWhenScreenOn && other.user == user && Objects.equals(other.automaticRules, automaticRules) - && Objects.equals(other.manualRule, manualRule); + && Objects.equals(other.manualRule, manualRule) + && other.suppressedVisualEffects == suppressedVisualEffects; } @Override public int hashCode() { - return Objects.hash(allowAlarms, allowMediaSystemOther, allowCalls, + return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents, - allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule); + allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule, + suppressedVisualEffects); } private static String toDayList(int[] days) { @@ -464,13 +488,19 @@ public class ZenModeConfig implements Parcelable { rt.allowCallsFrom = DEFAULT_SOURCE; rt.allowMessagesFrom = DEFAULT_SOURCE; } + // continue to read even though we now have suppressedVisualEffects, in case + // we need to revert to users' previous settings rt.allowWhenScreenOff = safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF); rt.allowWhenScreenOn = safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON); rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS); - rt.allowMediaSystemOther = safeBoolean(parser, ALLOW_ATT_MEDIA_SYSTEM_OTHER, - DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER); + rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA, + DEFAULT_ALLOW_MEDIA); + rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM); + } else if (DISALLOW_TAG.equals(tag)) { + rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS, + DEFAULT_SUPPRESSED_VISUAL_EFFECTS); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); } else if (AUTOMATIC_TAG.equals(tag)) { @@ -502,9 +532,14 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff)); out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn)); out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms)); - out.attribute(null, ALLOW_ATT_MEDIA_SYSTEM_OTHER, Boolean.toString(allowMediaSystemOther)); + out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia)); + out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem)); out.endTag(null, ALLOW_TAG); + out.startTag(null, DISALLOW_TAG); + out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects)); + out.endTag(null, DISALLOW_TAG); + if (manualRule != null) { out.startTag(null, MANUAL_TAG); writeRuleXml(manualRule, out); @@ -689,18 +724,14 @@ public class ZenModeConfig implements Parcelable { if (allowRepeatCallers) { priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; } - int suppressedVisualEffects = 0; - if (!allowWhenScreenOff) { - suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; - } - if (!allowWhenScreenOn) { - suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; - } if (allowAlarms) { priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; } - if (allowMediaSystemOther) { - priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER; + if (allowMedia) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; + } + if (allowSystem) { + priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; } priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); @@ -743,8 +774,8 @@ public class ZenModeConfig implements Parcelable { public void applyNotificationPolicy(Policy policy) { if (policy == null) return; allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0; - allowMediaSystemOther = (policy.priorityCategories - & Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0; + allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; + allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; @@ -755,10 +786,7 @@ public class ZenModeConfig implements Parcelable { allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders, allowMessagesFrom); if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { - allowWhenScreenOff = - (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_OFF) == 0; - allowWhenScreenOn = - (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0; + suppressedVisualEffects = policy.suppressedVisualEffects; } } @@ -1415,7 +1443,8 @@ public class ZenModeConfig implements Parcelable { } /** - * Determines whether dnd behavior should mute all notification sounds + * Determines whether dnd behavior should mute all notification/ringer sounds + * (sounds associated with ringer volume discluding system) */ public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy policy) { @@ -1434,7 +1463,7 @@ public class ZenModeConfig implements Parcelable { } /** - * Determines whether dnd behavior should mute all notification sounds + * Determines whether dnd behavior should mute all sounds controlled by ringer */ public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) { return !config.allowReminders && !config.allowCalls && !config.allowMessages @@ -1445,7 +1474,7 @@ public class ZenModeConfig implements Parcelable { * Determines whether all dnd mutes all sounds */ public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) { - return !config.allowAlarms && !config.allowMediaSystemOther + return !config.allowAlarms && !config.allowMedia && !config.allowSystem && areAllPriorityOnlyNotificationZenSoundsMuted(config); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 8588df7f136a01c4a984d69043f548119de9cd17..a1327301da67f12269a5cf2436a075e0a75f60c2 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -788,8 +788,8 @@ public abstract class WallpaperService extends Service { com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, - Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets, - mDisplayCutout, mInputChannel) < 0) { + Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets, + mOutsets, mDisplayCutout, mInputChannel) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 674f809ef0f57375e162c2d0714e6e90d8c45d29..70dfef574ca5323d1018af05d11e035925222d73 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -20,7 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Service; import android.content.Intent; -import android.content.pm.PackageManager; +import android.content.PermissionChecker; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -174,8 +174,8 @@ public abstract class RecognitionService extends Service { */ private boolean checkPermissions(IRecognitionListener listener) { if (DBG) Log.d(TAG, "checkPermissions"); - if (RecognitionService.this.checkCallingOrSelfPermission(android.Manifest.permission. - RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { + if (PermissionChecker.checkCallingOrSelfPermission(this, + android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) { return true; } try { diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java index c211e2dba3c0c0794bdc20900618804bf7950235..b74019373f572b7feffd5823c5ae58dbfba08a23 100644 --- a/core/java/android/text/PrecomputedText.java +++ b/core/java/android/text/PrecomputedText.java @@ -211,7 +211,8 @@ public class PrecomputedText implements Spanned { return mHyphenationFrequency; } - private boolean isSameTextMetricsInternal(@NonNull TextPaint paint, + /** @hide */ + public boolean isSameTextMetricsInternal(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { return mTextDir == textDir @@ -247,6 +248,23 @@ public class PrecomputedText implements Spanned { mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir, mBreakStrategy, mHyphenationFrequency); } + + @Override + public String toString() { + return "{" + + "textSize=" + mPaint.getTextSize() + + ", textScaleX=" + mPaint.getTextScaleX() + + ", textSkewX=" + mPaint.getTextSkewX() + + ", letterSpacing=" + mPaint.getLetterSpacing() + + ", textLocale=" + mPaint.getTextLocales() + + ", typeface=" + mPaint.getTypeface() + + ", variationSettings=" + mPaint.getFontVariationSettings() + + ", elegantTextHeight=" + mPaint.isElegantTextHeight() + + ", textDir=" + mTextDir + + ", breakStrategy=" + mBreakStrategy + + ", hyphenationFrequency=" + mHyphenationFrequency + + "}"; + } }; // The original text. diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index eba9129e81be5571b2f0caa73e05a081a10feb9f..94025ef96f10d658ce0eec485ec9ae5228c1e9cb 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -159,9 +159,9 @@ public class DateFormat { private static Locale sIs24HourLocale; private static boolean sIs24Hour; - /** - * Returns true if user preference is set to 24-hour format. + * Returns true if times should be formatted as 24 hour times, false if times should be + * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences. * @param context the context to use for the content resolver * @return true if 24 hour time format is selected, false otherwise. */ @@ -170,7 +170,8 @@ public class DateFormat { } /** - * Returns true if user preference with the given user handle is set to 24-hour format. + * Returns true if times should be formatted as 24 hour times, false if times should be + * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences. * @param context the context to use for the content resolver * @param userHandle the user handle of the user to query. * @return true if 24 hour time format is selected, false otherwise. diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 1ead0b49bbed53a22c60a4e5117a4e31f8e4b50b..38ab6f24f41d3022219e14a9ceea28a4d373de27 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -37,13 +37,13 @@ public class FeatureFlagUtils { private static final Map DEFAULT_FLAGS; static { DEFAULT_FLAGS = new HashMap<>(); - DEFAULT_FLAGS.put("settings_connected_device_v2", "true"); DEFAULT_FLAGS.put("settings_battery_v2", "true"); DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); DEFAULT_FLAGS.put("settings_zone_picker_v2", "true"); DEFAULT_FLAGS.put("settings_about_phone_v2", "true"); DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false"); DEFAULT_FLAGS.put("settings_data_usage_v2", "false"); + DEFAULT_FLAGS.put("settings_audio_switcher", "false"); } /** diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java index 402bef914421f838f55f9e7e263c190a3893460c..cc9991a9be20c76ea5ef173c0f1be9e6e2869a0d 100644 --- a/core/java/android/util/LauncherIcons.java +++ b/core/java/android/util/LauncherIcons.java @@ -110,9 +110,9 @@ public final class LauncherIcons { Drawable badgeColor = sysRes.getDrawable( com.android.internal.R.drawable.ic_corp_icon_badge_color) .getConstantState().newDrawable().mutate(); - badgeColor.setTint(backgroundColor); Drawable badgeForeground = sysRes.getDrawable(foregroundRes); + badgeForeground.setTint(backgroundColor); Drawable[] drawables = base == null ? new Drawable[] {badgeShadow, badgeColor, badgeForeground } diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java index cd8b097cd2f196b8db59e109649ab5e7664b4741..9f115eba442cd17f640e95a4c8410d545c0e88ba 100644 --- a/core/java/android/util/RecurrenceRule.java +++ b/core/java/android/util/RecurrenceRule.java @@ -99,6 +99,7 @@ public class RecurrenceRule implements Parcelable { start = convertZonedDateTime(BackupUtils.readString(in)); end = convertZonedDateTime(BackupUtils.readString(in)); period = convertPeriod(BackupUtils.readString(in)); + break; default: throw new ProtocolException("Unknown version " + version); } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 62222b5764e783abc4b44e63819dbefab5e1e1a4..533d72590f0ae2b6c2d1591d6e26d921b2ad4141 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -213,7 +213,9 @@ public class ApkSignatureSchemeV2Verifier { byte[] verityRootHash = null; if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { - verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength( + verityDigest, apk.length(), signatureInfo); } return new VerifiedSigner( diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 9436b296e9e24a3f1a3f83774c913f16045bd6b4..4431bcef1ff4a4880750db49137aec7816829bad 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -165,7 +165,7 @@ public class ApkSignatureSchemeV3Verifier { private static VerifiedSigner verify( RandomAccessFile apk, SignatureInfo signatureInfo, - boolean doVerifyIntegrity) throws SecurityException { + boolean doVerifyIntegrity) throws SecurityException, IOException { int signerCount = 0; Map contentDigests = new ArrayMap<>(); VerifiedSigner result = null; @@ -214,7 +214,9 @@ public class ApkSignatureSchemeV3Verifier { } if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { - result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength( + verityDigest, apk.length(), signatureInfo); } return result; diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 40db758e1d4bc1847f7c157a0edc91557aea262d..1c67434b2609587f744971bce6665c897b1c4d81 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -285,11 +285,46 @@ final class ApkSigningBlockUtils { return result; } + /** + * Return the verity digest only if the length of digest content looks correct. + * When verity digest is generated, the last incomplete 4k chunk is padded with 0s before + * hashing. This means two almost identical APKs with different number of 0 at the end will have + * the same verity digest. To avoid this problem, the length of the source content (excluding + * Signing Block) is appended to the verity digest, and the digest is returned only if the + * length is consistent to the current APK. + */ + static byte[] parseVerityDigestAndVerifySourceLength( + byte[] data, long fileSize, SignatureInfo signatureInfo) throws SecurityException { + // FORMAT: + // OFFSET DATA TYPE DESCRIPTION + // * @+0 bytes uint8[32] Merkle tree root hash of SHA-256 + // * @+32 bytes int64 Length of source data + int kRootHashSize = 32; + int kSourceLengthSize = 8; + + if (data.length != kRootHashSize + kSourceLengthSize) { + throw new SecurityException("Verity digest size is wrong: " + data.length); + } + ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + buffer.position(kRootHashSize); + long expectedSourceLength = buffer.getLong(); + + long signingBlockSize = signatureInfo.centralDirOffset + - signatureInfo.apkSigningBlockOffset; + if (expectedSourceLength != fileSize - signingBlockSize) { + throw new SecurityException("APK content size did not verify"); + } + + return Arrays.copyOfRange(data, 0, kRootHashSize); + } + private static void verifyIntegrityForVerityBasedAlgorithm( - byte[] expectedRootHash, + byte[] expectedDigest, RandomAccessFile apk, SignatureInfo signatureInfo) throws SecurityException { try { + byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest, + apk.length(), signatureInfo); ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk, signatureInfo, new ByteBufferFactory() { @Override @@ -373,9 +408,9 @@ final class ApkSigningBlockUtils { static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201; static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202; static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301; - static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0411; - static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0413; - static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0415; + static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0421; + static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423; + static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425; static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 1caea57731609de9129d076fd818d9aa40493bc4..31db213ea6f5e0c85ddd59bfc2e8f04b92aad074 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -80,6 +80,7 @@ public final class Choreographer { // Prints debug messages about jank which was detected (low volume). private static final boolean DEBUG_JANK = false; + private static final boolean OPTS_INPUT = SystemProperties.getBoolean("persist.vendor.qti.inputopts.enable",false); // Prints debug messages about every frame and callback registered (high volume). private static final boolean DEBUG_FRAMES = false; @@ -140,6 +141,11 @@ public final class Choreographer { private static final int MSG_DO_SCHEDULE_VSYNC = 1; private static final int MSG_DO_SCHEDULE_CALLBACK = 2; + private static final int MOTION_EVENT_ACTION_DOWN = 0; + private static final int MOTION_EVENT_ACTION_UP = 1; + private static final int MOTION_EVENT_ACTION_MOVE = 2; + private static final int MOTION_EVENT_ACTION_CANCEL = 3; + // All frame callbacks posted by applications have this token. private static final Object FRAME_CALLBACK_TOKEN = new Object() { public String toString() { return "FRAME_CALLBACK_TOKEN"; } @@ -165,7 +171,10 @@ public final class Choreographer { private long mFrameIntervalNanos; private boolean mDebugPrintNextFrameTimeDelta; private int mFPSDivisor = 1; - + private int mTouchMoveNum = -1; + private int mMotionEventType = -1; + private boolean mConsumedMove = false; + private boolean mConsumedDown = false; /** * Contains information about the current frame for jank-tracking, * mainly timings of key events along with a bit of metadata about @@ -263,6 +272,16 @@ public final class Choreographer { return sSfThreadInstance.get(); } + /** + * {@hide} + */ + public void setMotionEventInfo(int motionEventType, int touchMoveNum) { + synchronized(this) { + mTouchMoveNum = touchMoveNum; + mMotionEventType = motionEventType; + } + } + /** Destroys the calling thread's choreographer * @hide */ @@ -579,6 +598,42 @@ public final class Choreographer { private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; + if (OPTS_INPUT) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "scheduleFrameLocked-mMotionEventType:" + mMotionEventType + " mTouchMoveNum:" + mTouchMoveNum + + " mConsumedDown:" + mConsumedDown + " mConsumedMove:" + mConsumedMove); + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + synchronized(this) { + switch(mMotionEventType) { + case MOTION_EVENT_ACTION_DOWN: + mConsumedMove = false; + if (!mConsumedDown) { + Message msg = mHandler.obtainMessage(MSG_DO_FRAME); + msg.setAsynchronous(true); + mHandler.sendMessageAtFrontOfQueue(msg); + mConsumedDown = true; + return; + } + break; + case MOTION_EVENT_ACTION_MOVE: + mConsumedDown = false; + if ((mTouchMoveNum == 1) && !mConsumedMove) { + Message msg = mHandler.obtainMessage(MSG_DO_FRAME); + msg.setAsynchronous(true); + mHandler.sendMessageAtFrontOfQueue(msg); + mConsumedMove = true; + return; + } + break; + case MOTION_EVENT_ACTION_UP: + case MOTION_EVENT_ACTION_CANCEL: + mConsumedMove = false; + mConsumedDown = false; + break; + default: + break; + } + } + } if (USE_VSYNC) { if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame on vsync."); diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index f5b7068998d6de79a6091a4d83e45a83bcf08e86..bb16afddcd63ec81f51fa462ac2b3444eea666b4 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -89,7 +89,21 @@ public final class DisplayCutout { private final Rect mSafeInsets; private final Region mBounds; - private final Size mFrameSize; + private final Size mFrameSize; // TODO: move frameSize, calculateRelativeTo, etc. into WM. + + /** + * Creates a DisplayCutout instance. + * + * @param safeInsets the insets from each edge which avoid the display cutout as returned by + * {@link #getSafeInsetTop()} etc. + * @param bounds the bounds of the display cutout as returned by {@link #getBounds()}. + */ + // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) + public DisplayCutout(Rect safeInsets, Region bounds) { + this(safeInsets != null ? new Rect(safeInsets) : ZERO_RECT, + bounds != null ? Region.obtain(bounds) : Region.obtain(), + null /* frameSize */); + } /** * Creates a DisplayCutout instance. @@ -114,28 +128,28 @@ public final class DisplayCutout { return mSafeInsets.equals(ZERO_RECT); } - /** Returns the inset from the top which avoids the display cutout. */ + /** Returns the inset from the top which avoids the display cutout in pixels. */ public int getSafeInsetTop() { return mSafeInsets.top; } - /** Returns the inset from the bottom which avoids the display cutout. */ + /** Returns the inset from the bottom which avoids the display cutout in pixels. */ public int getSafeInsetBottom() { return mSafeInsets.bottom; } - /** Returns the inset from the left which avoids the display cutout. */ + /** Returns the inset from the left which avoids the display cutout in pixels. */ public int getSafeInsetLeft() { return mSafeInsets.left; } - /** Returns the inset from the right which avoids the display cutout. */ + /** Returns the inset from the right which avoids the display cutout in pixels. */ public int getSafeInsetRight() { return mSafeInsets.right; } /** - * Returns the safe insets in a rect. + * Returns the safe insets in a rect in pixel units. * * @return a rect which is set to the safe insets. * @hide @@ -148,7 +162,7 @@ public final class DisplayCutout { * Returns the bounding region of the cutout. * * @return the bounding region of the cutout. Coordinates are relative - * to the top-left corner of the content view. + * to the top-left corner of the content view and in pixel units. */ public Region getBounds() { return Region.obtain(mBounds); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 4adcb8f15be731960eb97d5355cdeb877fce78aa..77a74e260811e99ebab0e8bfd20bc179ef26c019 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -185,13 +185,6 @@ interface IWindowManager */ void setScreenCaptureDisabled(int userId, boolean disabled); - /** - * Testing and debugging infrastructure for writing surface events - * to given FD. See RemoteSurfaceTrace.java or Wm.java for format. - */ - void enableSurfaceTrace(in ParcelFileDescriptor fd); - void disableSurfaceTrace(); - // These can only be called with the SET_ORIENTATION permission. /** * Update the current screen rotation based on the current state of @@ -290,6 +283,11 @@ interface IWindowManager */ oneway void setPipVisibility(boolean visible); + /** + * Called by System UI to enable or disable haptic feedback on the navigation bar buttons. + */ + void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled); + /** * Device has a software navigation bar (separate from the status bar). */ @@ -418,4 +416,10 @@ interface IWindowManager * Returns true if window trace is enabled. */ boolean isWindowTraceEnabled(); + + /** + * Requests that the WindowManager sends WindowManagerPolicy#ACTION_USER_ACTIVITY_NOTIFICATION + * on the next user activity. + */ + void requestUserActivityNotification(); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index d7fd3299b9e34b1dba297beb83eacfaa4f0eeb47..d8a5609a3c981a700eeb2e79209de2be5c4161be 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -41,8 +41,8 @@ interface IWindowSession { in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets, out InputChannel outInputChannel); int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, in int layerStackId, out Rect outContentInsets, - out Rect outStableInsets, out Rect outOutsets, + in int viewVisibility, in int layerStackId, out Rect outFrame, + out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets, out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel); int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets); diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index c566a653da422488e02d148f2be0f19c077d694a..be3d9539a6c7341a9fd2cdde5a57bf87ac7a5d07 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -43,6 +43,7 @@ public abstract class InputEventReceiver { // Map from InputEvent sequence numbers to dispatcher sequence numbers. private final SparseIntArray mSeqMap = new SparseIntArray(); + Choreographer mChoreographer; private static native long nativeInit(WeakReference receiver, InputChannel inputChannel, MessageQueue messageQueue); @@ -192,6 +193,19 @@ public abstract class InputEventReceiver { onBatchedInputEventPending(); } + // Called from native code. + @SuppressWarnings("unused") + private void dispatchMotionEventInfo(int motionEventType, int touchMoveNum) { + try { + if (mChoreographer == null) + mChoreographer = Choreographer.getInstance(); + + if (mChoreographer != null) + mChoreographer.setMotionEventInfo(motionEventType, touchMoveNum); + } catch (Exception e) { + Log.e(TAG, "cannot invoke setMotionEventInfo."); + } + } public static interface Factory { public InputEventReceiver createInputEventReceiver( InputChannel inputChannel, Looper looper); diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index fbba8abff30453a0cc981302cd03dba3d24b3191..7a10364708941de3145ad86a4733abdf82614499 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.Nullable; +import android.app.AppOpsManager; import android.app.Notification; import android.content.Context; import android.content.res.Resources; @@ -25,6 +26,7 @@ import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.util.ArraySet; import android.util.AttributeSet; import android.widget.ImageView; import android.widget.RemoteViews; @@ -49,10 +51,15 @@ public class NotificationHeaderView extends ViewGroup { private View mHeaderText; private View mSecondaryHeaderText; private OnClickListener mExpandClickListener; + private OnClickListener mAppOpsListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); private ImageView mExpandButton; private CachingIconView mIcon; private View mProfileBadge; + private View mOverlayIcon; + private View mCameraIcon; + private View mMicIcon; + private View mAppOps; private int mIconColor; private int mOriginalNotificationColor; private boolean mExpanded; @@ -108,6 +115,10 @@ public class NotificationHeaderView extends ViewGroup { mExpandButton = findViewById(com.android.internal.R.id.expand_button); mIcon = findViewById(com.android.internal.R.id.icon); mProfileBadge = findViewById(com.android.internal.R.id.profile_badge); + mCameraIcon = findViewById(com.android.internal.R.id.camera); + mMicIcon = findViewById(com.android.internal.R.id.mic); + mOverlayIcon = findViewById(com.android.internal.R.id.overlay); + mAppOps = findViewById(com.android.internal.R.id.app_ops); } @Override @@ -198,6 +209,11 @@ public class NotificationHeaderView extends ViewGroup { layoutRight = end - paddingEnd; end = layoutLeft = layoutRight - child.getMeasuredWidth(); } + if (child == mAppOps) { + int paddingEnd = mContentEndMargin; + layoutRight = end - paddingEnd; + end = layoutLeft = layoutRight - child.getMeasuredWidth(); + } if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) { int ltrLeft = layoutLeft; layoutLeft = getWidth() - layoutRight; @@ -252,15 +268,26 @@ public class NotificationHeaderView extends ViewGroup { } private void updateTouchListener() { - if (mExpandClickListener != null) { - mTouchListener.bindTouchRects(); + if (mExpandClickListener == null && mAppOpsListener == null) { + setOnTouchListener(null); + return; } + setOnTouchListener(mTouchListener); + mTouchListener.bindTouchRects(); + } + + /** + * Sets onclick listener for app ops icons. + */ + public void setAppOpsOnClickListener(OnClickListener l) { + mAppOpsListener = l; + mAppOps.setOnClickListener(mAppOpsListener); + updateTouchListener(); } @Override public void setOnClickListener(@Nullable OnClickListener l) { mExpandClickListener = l; - setOnTouchListener(mExpandClickListener != null ? mTouchListener : null); mExpandButton.setOnClickListener(mExpandClickListener); updateTouchListener(); } @@ -289,6 +316,22 @@ public class NotificationHeaderView extends ViewGroup { updateExpandButton(); } + /** + * Shows or hides 'app op in use' icons based on app usage. + */ + public void showAppOpsIcons(ArraySet appOps) { + if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null || appOps == null) { + return; + } + + mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) + ? View.VISIBLE : View.GONE); + mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA) + ? View.VISIBLE : View.GONE); + mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO) + ? View.VISIBLE : View.GONE); + } + private void updateExpandButton() { int drawableId; int contentDescriptionId; @@ -335,6 +378,7 @@ public class NotificationHeaderView extends ViewGroup { private final ArrayList mTouchRects = new ArrayList<>(); private Rect mExpandButtonRect; + private Rect mAppOpsRect; private int mTouchSlop; private boolean mTrackGesture; private float mDownX; @@ -347,6 +391,7 @@ public class NotificationHeaderView extends ViewGroup { mTouchRects.clear(); addRectAroundView(mIcon); mExpandButtonRect = addRectAroundView(mExpandButton); + mAppOpsRect = addRectAroundView(mAppOps); addWidthRect(); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -368,16 +413,18 @@ public class NotificationHeaderView extends ViewGroup { private Rect getRectAroundView(View view) { float size = 48 * getResources().getDisplayMetrics().density; + float width = Math.max(size, view.getWidth()); + float height = Math.max(size, view.getHeight()); final Rect r = new Rect(); if (view.getVisibility() == GONE) { view = getFirstChildNotGone(); - r.left = (int) (view.getLeft() - size / 2.0f); + r.left = (int) (view.getLeft() - width / 2.0f); } else { - r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - size / 2.0f); + r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - width / 2.0f); } - r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - size / 2.0f); - r.bottom = (int) (r.top + size); - r.right = (int) (r.left + size); + r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - height / 2.0f); + r.bottom = (int) (r.top + height); + r.right = (int) (r.left + width); return r; } @@ -405,6 +452,11 @@ public class NotificationHeaderView extends ViewGroup { break; case MotionEvent.ACTION_UP: if (mTrackGesture) { + if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y) + || mAppOpsRect.contains((int) mDownX, (int) mDownY))) { + mAppOps.performClick(); + return true; + } mExpandButton.performClick(); } break; diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java index d597e597b119403ccd6c37c36e42764cade35de3..a864e550c25674275c6727ee8a2f2e97471bc070 100644 --- a/core/java/android/view/RemoteAnimationAdapter.java +++ b/core/java/android/view/RemoteAnimationAdapter.java @@ -52,6 +52,9 @@ public class RemoteAnimationAdapter implements Parcelable { private final long mDuration; private final long mStatusBarTransitionDelay; + /** @see #getCallingPid */ + private int mCallingPid; + /** * @param runner The interface that gets notified when we actually need to start the animation. * @param duration The duration of the animation. @@ -83,6 +86,20 @@ public class RemoteAnimationAdapter implements Parcelable { return mStatusBarTransitionDelay; } + /** + * To be called by system_server to keep track which pid is running this animation. + */ + public void setCallingPid(int pid) { + mCallingPid = pid; + } + + /** + * @return The pid of the process running the animation. + */ + public int getCallingPid() { + return mCallingPid; + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java index 381f6926a1e8b122c350d7278a42b749194c023a..8def43512e513513b7af6cf56fc8473b5e44dd06 100644 --- a/core/java/android/view/RemoteAnimationDefinition.java +++ b/core/java/android/view/RemoteAnimationDefinition.java @@ -70,6 +70,16 @@ public class RemoteAnimationDefinition implements Parcelable { mTransitionAnimationMap = in.readSparseArray(null /* loader */); } + /** + * To be called by system_server to keep track which pid is running the remote animations inside + * this definition. + */ + public void setCallingPid(int pid) { + for (int i = mTransitionAnimationMap.size() - 1; i >= 0; i--) { + mTransitionAnimationMap.valueAt(i).setCallingPid(pid); + } + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index facf575872ed6a6cc35ee0450f88f1f87675815c..75cdd49fccab17b57fbab812c8e80b4a9d46ca2a 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -109,9 +109,14 @@ public class RemoteAnimationTarget implements Parcelable { */ public final WindowConfiguration windowConfiguration; + /** + * Whether the task is not presented in Recents UI. + */ + public boolean isNotInRecents; + public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, - Rect sourceContainerBounds, WindowConfiguration windowConfig) { + Rect sourceContainerBounds, WindowConfiguration windowConfig, boolean isNotInRecents) { this.mode = mode; this.taskId = taskId; this.leash = leash; @@ -122,6 +127,7 @@ public class RemoteAnimationTarget implements Parcelable { this.position = new Point(position); this.sourceContainerBounds = new Rect(sourceContainerBounds); this.windowConfiguration = windowConfig; + this.isNotInRecents = isNotInRecents; } public RemoteAnimationTarget(Parcel in) { @@ -135,6 +141,7 @@ public class RemoteAnimationTarget implements Parcelable { position = in.readParcelable(null); sourceContainerBounds = in.readParcelable(null); windowConfiguration = in.readParcelable(null); + isNotInRecents = in.readBoolean(); } @Override @@ -154,6 +161,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeParcelable(position, 0 /* flags */); dest.writeParcelable(sourceContainerBounds, 0 /* flags */); dest.writeParcelable(windowConfiguration, 0 /* flags */); + dest.writeBoolean(isNotInRecents); } public static final Creator CREATOR diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 8830c90addacd054faaa5945b35e6dd0443eb837..d3b1e5c52596aac0efd6968256987b0ceb8349d7 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -396,7 +396,44 @@ public class Surface implements Parcelable { synchronized (mLock) { checkNotReleasedLocked(); if (mHwuiContext == null) { - mHwuiContext = new HwuiContext(); + mHwuiContext = new HwuiContext(false); + } + return mHwuiContext.lockCanvas( + nativeGetWidth(mNativeObject), + nativeGetHeight(mNativeObject)); + } + } + + /** + * Gets a {@link Canvas} for drawing into this surface that supports wide color gamut. + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * Unlike {@link #lockCanvas(Rect)} and {@link #lockHardwareCanvas()}, + * this will return a hardware-accelerated canvas that supports wide color gamut. + * See the + * unsupported drawing operations for a list of what is and isn't + * supported in a hardware-accelerated canvas. It is also required to + * fully cover the surface every time {@link #lockHardwareCanvas()} is + * called as the buffer is not preserved between frames. Partial updates + * are not supported. + * + * @return A canvas for drawing into the surface. + * + * @throws IllegalStateException If the canvas cannot be locked. + * + * @hide + */ + public Canvas lockHardwareWideColorGamutCanvas() { + synchronized (mLock) { + checkNotReleasedLocked(); + if (mHwuiContext != null && !mHwuiContext.isWideColorGamut()) { + mHwuiContext.destroy(); + mHwuiContext = null; + } + if (mHwuiContext == null) { + mHwuiContext = new HwuiContext(true); } return mHwuiContext.lockCanvas( nativeGetWidth(mNativeObject), @@ -829,11 +866,14 @@ public class Surface implements Parcelable { private final RenderNode mRenderNode; private long mHwuiRenderer; private DisplayListCanvas mCanvas; + private final boolean mIsWideColorGamut; - HwuiContext() { + HwuiContext(boolean isWideColorGamut) { mRenderNode = RenderNode.create("HwuiCanvas", null); mRenderNode.setClipToBounds(false); - mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); + mIsWideColorGamut = isWideColorGamut; + mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject, + isWideColorGamut); } Canvas lockCanvas(int width, int height) { @@ -864,9 +904,13 @@ public class Surface implements Parcelable { mHwuiRenderer = 0; } } + + boolean isWideColorGamut() { + return mIsWideColorGamut; + } } - private static native long nHwuiCreate(long rootNode, long surface); + private static native long nHwuiCreate(long rootNode, long surface, boolean isWideColorGamut); private static native void nHwuiSetSurface(long renderer, long surface); private static native void nHwuiDraw(long renderer); private static native void nHwuiDestroy(long renderer); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index bd7f8e5419ebe4394e7cc12fcae1437055626458..b7524fb1a5220ff4a950dce6664604a8768b0e1d 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -152,6 +152,7 @@ public class SurfaceControl implements Parcelable { private static native void nativeSeverChildren(long transactionObj, long nativeObject); private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject, int scalingMode); + private static native void nativeDestroy(long transactionObj, long nativeObject); private static native IBinder nativeGetHandle(long nativeObject); private static native boolean nativeGetTransformToDisplayInverse(long nativeObject); @@ -763,18 +764,14 @@ public class SurfaceControl implements Parcelable { } public void deferTransactionUntil(IBinder handle, long frame) { - if (frame > 0) { - synchronized(SurfaceControl.class) { - sGlobalTransaction.deferTransactionUntil(this, handle, frame); - } + synchronized(SurfaceControl.class) { + sGlobalTransaction.deferTransactionUntil(this, handle, frame); } } public void deferTransactionUntil(Surface barrier, long frame) { - if (frame > 0) { - synchronized(SurfaceControl.class) { - sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame); - } + synchronized(SurfaceControl.class) { + sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame); } } @@ -1479,6 +1476,9 @@ public class SurfaceControl implements Parcelable { public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle, long frameNumber) { + if (frameNumber < 0) { + return this; + } sc.checkNotReleased(); nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber); return this; @@ -1486,6 +1486,9 @@ public class SurfaceControl implements Parcelable { public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface, long frameNumber) { + if (frameNumber < 0) { + return this; + } sc.checkNotReleased(); nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject, barrierSurface.mNativeObject, frameNumber); @@ -1570,6 +1573,16 @@ public class SurfaceControl implements Parcelable { return this; } + /** + * Same as {@link #destroy()} except this is invoked in a transaction instead of + * immediately. + */ + public Transaction destroy(SurfaceControl sc) { + sc.checkNotReleased(); + nativeDestroy(mNativeObject, sc.mNativeObject); + return this; + } + public Transaction setDisplaySurface(IBinder displayToken, Surface surface) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index db19681840d78076d826ed798480c1b75cd80e00..8d076f75ddbb63fba3dc790403206c6c6ba9c518 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -20,11 +20,13 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.app.ActivityManager; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Build; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -1067,7 +1069,14 @@ public final class ThreadedRenderer { mAppContext = context.getApplicationContext(); initSched(renderProxy); - initGraphicsStats(); + + if (mAppContext != null) { + final boolean appDebuggable = + (mAppContext.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) + != 0; + nSetDebuggingEnabled(appDebuggable || Build.IS_DEBUGGABLE); + initGraphicsStats(); + } } private void initSched(long renderProxy) { @@ -1194,4 +1203,5 @@ public final class ThreadedRenderer { private static native void nSetHighContrastText(boolean enabled); // For temporary experimentation b/66945974 private static native void nHackySetRTAnimationsEnabled(boolean enabled); + private static native void nSetDebuggingEnabled(boolean enabled); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f61b6528bd0e093ed4dd7f4f60953389bc3573c8..bf0e2ebf6debecbbdbf838302aa7d8995ec25e07 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2953,6 +2953,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_NO_REVEAL_ON_FOCUS * 1 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT * 1 PFLAG3_SCREEN_READER_FOCUSABLE + * 1 PFLAG3_AGGREGATED_VISIBLE + * 1 PFLAG3_AUTOFILLID_EXPLICITLY_SET + * 1 available * |-------|-------|-------|-------| */ @@ -3243,6 +3246,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000; + /** + * Used to indicate that {@link #mAutofillId} was explicitly set through + * {@link #setAutofillId(AutofillId)}. + */ + private static final int PFLAG3_AUTOFILLID_EXPLICITLY_SET = 0x40000000; + /* End of masks for mPrivateFlags3 */ /** @@ -7280,16 +7289,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * If this view is a visually distinct portion of a window, for example the content view of - * a fragment that is replaced, it is considered a pane for accessibility purposes. In order - * for accessibility services to understand the views role, and to announce its title as - * appropriate, such views should have pane titles. + * Visually distinct portion of a window with window-like semantics are considered panes for + * accessibility purposes. One example is the content view of a fragment that is replaced. + * In order for accessibility services to understand a pane's window-like behavior, panes + * should have descriptive titles. Views with pane titles produce {@link AccessibilityEvent}s + * when they appear, disappear, or change title. * - * @param accessibilityPaneTitle The pane's title. + * @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this + * View is not a pane. * * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)} */ - public void setAccessibilityPaneTitle(CharSequence accessibilityPaneTitle) { + public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) { if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) { mAccessibilityPaneTitle = accessibilityPaneTitle; notifyViewAccessibilityStateChangedIfNeeded( @@ -7304,7 +7315,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * {@see #setAccessibilityPaneTitle}. */ - public CharSequence getAccessibilityPaneTitle() { + @Nullable public CharSequence getAccessibilityPaneTitle() { return mAccessibilityPaneTitle; } @@ -7969,6 +7980,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void onProvideVirtualStructureCompat(ViewStructure structure, boolean forAutofill) { final AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if (provider != null) { + if (android.view.autofill.Helper.sVerbose && forAutofill) { + Log.v(VIEW_LOG_TAG, "onProvideVirtualStructureCompat() for " + this); + } + final AccessibilityNodeInfo info = createAccessibilityNodeInfo(); structure.setChildCount(1); final ViewStructure root = structure.newChild(0); @@ -8205,16 +8220,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @throws IllegalArgumentException if the id is an autofill id associated with a virtual view. */ public void setAutofillId(@Nullable AutofillId id) { + // TODO(b/37566627): add unit / CTS test for all possible combinations below if (android.view.autofill.Helper.sVerbose) { Log.v(VIEW_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id); } if (isAttachedToWindow()) { throw new IllegalStateException("Cannot set autofill id when view is attached"); } - if (id.isVirtual()) { + if (id != null && id.isVirtual()) { throw new IllegalStateException("Cannot set autofill id assigned to virtual views"); } + if (id == null && (mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) == 0) { + // Ignore reset because it was never explicitly set before. + return; + } mAutofillId = id; + if (id != null) { + mAutofillViewId = id.getViewId(); + mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET; + } else { + mAutofillViewId = NO_ID; + mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET; + } } /** @@ -13982,7 +14009,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // setFlags invocation. shouldNotifyFocusableAvailable = true; } else { - if (hasFocus()) clearFocus(); + if (isFocused()) clearFocus(); } } @@ -18524,7 +18551,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Hence prevent the same autofill view id from being restored multiple times. ((BaseSavedState) state).mSavedData &= ~BaseSavedState.AUTOFILL_ID; - mAutofillViewId = baseState.mAutofillViewId; + if ((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) != 0) { + // Ignore when view already set it through setAutofillId(); + if (android.view.autofill.Helper.sDebug) { + Log.d(VIEW_LOG_TAG, "onRestoreInstanceState(): not setting autofillId to " + + baseState.mAutofillViewId + " because view explicitly set it to " + + mAutofillId); + } + } else { + mAutofillViewId = baseState.mAutofillViewId; + mAutofillId = null; // will be set on demand by getAutofillId() + } } } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index c5a94daaba530741cbd3ae91de40e4999ff61d3d..7a9de45cbbf8449c38da6fa21f91226bb7844dbc 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -303,6 +303,7 @@ public class ViewConfiguration { private final long mGlobalActionsKeyTimeout; private final float mVerticalScrollFactor; private final float mHorizontalScrollFactor; + private final boolean mShowMenuShortcutsWhenKeyboardPresent; private boolean sHasPermanentMenuKey; private boolean sHasPermanentMenuKeySet; @@ -335,6 +336,7 @@ public class ViewConfiguration { mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT; mHorizontalScrollFactor = HORIZONTAL_SCROLL_FACTOR; mVerticalScrollFactor = VERTICAL_SCROLL_FACTOR; + mShowMenuShortcutsWhenKeyboardPresent = false; } /** @@ -428,6 +430,10 @@ public class ViewConfiguration { com.android.internal.R.dimen.config_horizontalScrollFactor); mVerticalScrollFactor = res.getDimensionPixelSize( com.android.internal.R.dimen.config_verticalScrollFactor); + + mShowMenuShortcutsWhenKeyboardPresent = res.getBoolean( + com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent); + } /** @@ -909,6 +915,15 @@ public class ViewConfiguration { return sHasPermanentMenuKey; } + /** + * Check if shortcuts should be displayed in menus. + * + * @return {@code True} if shortcuts should be displayed in menus. + */ + public boolean shouldShowMenuShortcutsWhenKeyboardPresent() { + return mShowMenuShortcutsWhenKeyboardPresent; + } + /** * @hide * @return Whether or not marquee should use fading edges. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 683c7f5ad96a46d64b3c659f2df4088359a71091..c36ab5d1310aa268ccdebe548a061b7a5be8d75a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -96,6 +96,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import android.view.autofill.AutofillManager; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -756,7 +757,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, - getHostVisibility(), mDisplay.getDisplayId(), + getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); } catch (RemoteException e) { @@ -1714,8 +1715,8 @@ public final class ViewRootImpl implements ViewParent, desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else { - desiredWindowWidth = dipToPx(config.screenWidthDp); - desiredWindowHeight = dipToPx(config.screenHeightDp); + desiredWindowWidth = mWinFrame.width(); + desiredWindowHeight = mWinFrame.height(); } // We used to use the following condition to choose 32 bits drawing caches: @@ -4795,6 +4796,21 @@ public final class ViewRootImpl implements ViewParent, ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); } + if (action == MotionEvent.ACTION_DOWN && mView instanceof ViewGroup) { + // Upon motion event within app window, close autofill ui. + ViewGroup decorView = (ViewGroup) mView; + if (decorView.getChildCount() > 0) { + // We cannot use decorView's Context for querying AutofillManager: DecorView's + // context is based on Application Context, it would allocate a different + // AutofillManager instance. + AutofillManager afm = (AutofillManager) decorView.getChildAt(0).getContext() + .getSystemService(Context.AUTOFILL_MANAGER_SERVICE); + if (afm != null) { + afm.requestHideFillUi(); + } + } + } + if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) { mAttachInfo.mTooltipHost.hideTooltip(); } @@ -6461,18 +6477,24 @@ public final class ViewRootImpl implements ViewParent, params.backup(); mTranslator.translateWindowLayout(params); } + if (params != null) { if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params); - } - if (params != null && mOrigWindowType != params.type) { - // For compatibility with old apps, don't crash here. - if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - Slog.w(mTag, "Window type can not be changed after " - + "the window is added; ignoring change of " + mView); - params.type = mOrigWindowType; + if (mOrigWindowType != params.type) { + // For compatibility with old apps, don't crash here. + if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + Slog.w(mTag, "Window type can not be changed after " + + "the window is added; ignoring change of " + mView); + params.type = mOrigWindowType; + } + } + + if (mSurface.isValid()) { + params.frameNumber = mSurface.getNextFrameNumber(); } } + int relayoutResult = mWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c0a966602b0ab52bb56997b56f49a22aafc15c4a..2354f255c30b4f974e1f277f69fbb601d047514d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -2234,6 +2234,7 @@ public interface WindowManager extends ViewManager { * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER * @see DisplayCutout + * @see android.R.attr#layoutInDisplayCutoutMode */ @LayoutInDisplayCutoutMode public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; @@ -2370,6 +2371,13 @@ public interface WindowManager extends ViewManager { */ public long hideTimeoutMilliseconds = -1; + /** + * A frame number in which changes requested in this layout will be rendered. + * + * @hide + */ + public long frameNumber = -1; + /** * The color mode requested by this window. The target display may * not be able to honor the request. When the color mode is not set @@ -2543,6 +2551,7 @@ public interface WindowManager extends ViewManager { TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags); out.writeInt(mColorMode); out.writeLong(hideTimeoutMilliseconds); + out.writeLong(frameNumber); } public static final Parcelable.Creator CREATOR @@ -2599,6 +2608,7 @@ public interface WindowManager extends ViewManager { accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mColorMode = in.readInt(); hideTimeoutMilliseconds = in.readLong(); + frameNumber = in.readLong(); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -2799,6 +2809,10 @@ public interface WindowManager extends ViewManager { changes |= SURFACE_INSETS_CHANGED; } + // The frame number changing is only relevant in the context of other + // changes, and so we don't need to track it with a flag. + frameNumber = o.frameNumber; + if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) { hasManualSurfaceInsets = o.hasManualSurfaceInsets; changes |= SURFACE_INSETS_CHANGED; diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index a6f36bbf4ef4c819e555863800ecf0a5e70d7a80..23dc9da694a6033e0cd08e006a4ea62cc7b562e1 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -50,6 +50,12 @@ public interface WindowManagerPolicyConstants { int NAV_BAR_RIGHT = 1 << 1; int NAV_BAR_BOTTOM = 1 << 2; + /** + * Broadcast sent when a user activity is detected. + */ + String ACTION_USER_ACTIVITY_NOTIFICATION = + "android.intent.action.USER_ACTIVITY_NOTIFICATION"; + /** * Sticky broadcast of the current HDMI plugged state. */ diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index af6c701bd5b9e8c7c943f4ed048fc243a18813ad..fdd3f7370223936cd2595e4e2839291b55d3e39e 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3353,7 +3353,7 @@ public class AccessibilityNodeInfo implements Parcelable { } } } else { - parcel.writeInt(0); + parcel.writeLong(0); parcel.writeInt(0); } } @@ -3621,7 +3621,7 @@ public class AccessibilityNodeInfo implements Parcelable { } private static boolean isDefaultStandardAction(AccessibilityAction action) { - return action.mSerializationFlag != -1 && TextUtils.isEmpty(action.getLabel()); + return (action.mSerializationFlag != -1L) && TextUtils.isEmpty(action.getLabel()); } private static AccessibilityAction getActionSingleton(int actionId) { @@ -3651,7 +3651,7 @@ public class AccessibilityNodeInfo implements Parcelable { private void addStandardActions(long serializationIdMask) { long remainingIds = serializationIdMask; while (remainingIds > 0) { - final int id = 1 << Long.numberOfTrailingZeros(remainingIds); + final long id = 1L << Long.numberOfTrailingZeros(remainingIds); remainingIds &= ~id; AccessibilityAction action = getActionSingletonBySerializationFlag(id); addAction(action); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index a4261ebe16a013ade881a942dff6a958c87cf030..158c2ee0b2d079307a59c8a440b2052fe54bc7cc 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -358,6 +358,9 @@ public final class AutofillManager { @GuardedBy("mLock") @Nullable private ArraySet mFillableIds; + /** id of last requested autofill ui */ + @Nullable private AutofillId mIdShownFillUi; + /** * Views that were already "entered" - if they're entered again when the session is not active, * they're ignored @@ -983,6 +986,7 @@ public final class AutofillManager { * @param virtualId id identifying the virtual child inside the parent view. */ public void notifyViewExited(@NonNull View view, int virtualId) { + if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId); if (!hasAutofillFeature()) { return; } @@ -1547,6 +1551,7 @@ public final class AutofillManager { mTrackedViews = null; mFillableIds = null; mSaveTriggerId = null; + mIdShownFillUi = null; if (resetEnteredIds) { mEnteredIds = null; } @@ -1676,8 +1681,9 @@ public final class AutofillManager { if (client != null) { if (client.autofillClientRequestShowFillUi(anchor, width, height, - anchorBounds, presenter) && mCallback != null) { + anchorBounds, presenter)) { callback = mCallback; + mIdShownFillUi = id; } } } @@ -1944,10 +1950,23 @@ public final class AutofillManager { } } - private void requestHideFillUi(AutofillId id) { - final View anchor = findView(id); + /** @hide */ + public void requestHideFillUi() { + requestHideFillUi(mIdShownFillUi, true); + } + + private void requestHideFillUi(AutofillId id, boolean force) { + final View anchor = id == null ? null : findView(id); if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor); if (anchor == null) { + if (force) { + // When user taps outside autofill window, force to close fill ui even id does + // not match. + AutofillClient client = getClient(); + if (client != null) { + client.autofillClientRequestHideFillUi(); + } + } return; } requestHideFillUi(id, anchor); @@ -1963,7 +1982,8 @@ public final class AutofillManager { // service being uninstalled and the UI being dismissed. AutofillClient client = getClient(); if (client != null) { - if (client.autofillClientRequestHideFillUi() && mCallback != null) { + if (client.autofillClientRequestHideFillUi()) { + mIdShownFillUi = null; callback = mCallback; } } @@ -2171,6 +2191,7 @@ public final class AutofillManager { public int getRelevantEventTypes(int relevantEventTypes) { return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED + | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; } @@ -2229,6 +2250,12 @@ public final class AutofillManager { } } break; + case AccessibilityEvent.TYPE_VIEW_CLICKED: { + synchronized (mLock) { + notifyViewClicked(event.getWindowId(), event.getSourceNodeId()); + } + } break; + case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { final AutofillClient client = getClient(); if (client != null) { @@ -2300,6 +2327,22 @@ public final class AutofillManager { AutofillValue.forText(node.getText())); } + private void notifyViewClicked(int windowId, long nodeId) { + final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId); + if (!isVirtualNode(virtualId)) { + return; + } + final View view = findViewByAccessibilityId(windowId, nodeId); + if (view == null) { + return; + } + final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId); + if (node == null) { + return; + } + AutofillManager.this.notifyViewClicked(view, virtualId); + } + @GuardedBy("mLock") private void updateTrackedViewsLocked() { if (mTrackedViews != null) { @@ -2655,7 +2698,7 @@ public final class AutofillManager { public void requestHideFillUi(int sessionId, AutofillId id) { final AutofillManager afm = mAfm.get(); if (afm != null) { - afm.post(() -> afm.requestHideFillUi(id)); + afm.post(() -> afm.requestHideFillUi(id, false)); } } diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index eba91763acf1a9ad64e628881c14d17878590600..e5545405728d53a52558e92fdf0f2474268bc701 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.inputmethodservice.InputMethodService; import android.os.Bundle; import android.os.Handler; -import android.os.LocaleList; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -899,37 +898,4 @@ public interface InputConnection { */ boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags, @Nullable Bundle opts); - - /** - * Called by the input method to tell a hint about the locales of text to be committed. - * - *

    This is just a hint for editor authors (and the system) to choose better options when - * they have to disambiguate languages, like editor authors can do for input methods with - * {@link EditorInfo#hintLocales}.

    - * - *

    The language hint provided by this callback should have higher priority than - * {@link InputMethodSubtype#getLanguageTag()}, which cannot be updated dynamically.

    - * - *

    Note that in general it is discouraged for input method to specify - * {@link android.text.style.LocaleSpan} when inputting text, mainly because of application - * compatibility concerns.

    - *
      - *
    • When an existing text that already has {@link android.text.style.LocaleSpan} is being - * modified by both the input method and application, there is no reliable and easy way to - * keep track of who modified {@link android.text.style.LocaleSpan}. For instance, if the - * text was updated by JavaScript, it it highly likely that span information is completely - * removed, while some input method attempts to preserve spans if possible.
    • - *
    • There is no clear semantics regarding whether {@link android.text.style.LocaleSpan} - * means a weak (ignorable) hint or a strong hint. This becomes more problematic when - * multiple {@link android.text.style.LocaleSpan} instances are specified to the same - * text region, especially when those spans are conflicting.
    • - *
    - * @param languageHint list of languages sorted by the priority and/or probability - */ - default void reportLanguageHint(@NonNull LocaleList languageHint) { - // Intentionally empty. - // - // We need to have *some* default implementation for the source compatibility. - // See Bug 72127682 for details. - } } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index cbe6856bba410be8f96cbdbf3473f8642563dcf7..f671e22b492257b4ee1f6f131e1d40021687cc9f 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -16,10 +16,8 @@ package android.view.inputmethod; -import android.annotation.NonNull; import android.os.Bundle; import android.os.Handler; -import android.os.LocaleList; import android.view.KeyEvent; /** @@ -305,13 +303,4 @@ public class InputConnectionWrapper implements InputConnection { public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { return mTarget.commitContent(inputContentInfo, flags, opts); } - - /** - * {@inheritDoc} - * @throws NullPointerException if the target is {@code null}. - */ - @Override - public void reportLanguageHint(@NonNull LocaleList languageHint) { - mTarget.reportLanguageHint(languageHint); - } } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index c69543f6d2d8edad1c88c3b91859efe263e04af7..f0f30a0df8ba360e3a6ad25700054003d3d8d4df 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -261,8 +261,7 @@ public final class InputMethodInfo implements Parcelable { mIsDefaultResId = isDefaultResId; mIsAuxIme = isAuxIme; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; - // TODO(b/68948291): remove this meta-data before release. - mIsVrOnly = isVrOnly || service.serviceInfo.metaData.getBoolean("isVrOnly", false); + mIsVrOnly = isVrOnly; } InputMethodInfo(Parcel source) { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index a2280a4acd11c66fea49c22478bd18bf3554f1eb..4104728149704ede117702c2b57741e5b3c2b6d6 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1812,9 +1812,9 @@ public final class InputMethodManager { * when it was started, which allows it to perform this operation on * itself. * @param id The unique identifier for the new input method to be switched to. - * @deprecated Use {@link InputMethodService#setInputMethod(String)} instead. This method - * was intended for IME developers who should be accessing APIs through the service. APIs in - * this class are intended for app developers interacting with the IME. + * @deprecated Use {@link InputMethodService#switchInputMethod(String)} + * instead. This method was intended for IME developers who should be accessing APIs through + * the service. APIs in this class are intended for app developers interacting with the IME. */ @Deprecated public void setInputMethod(IBinder token, String id) { @@ -1841,7 +1841,7 @@ public final class InputMethodManager { * @param id The unique identifier for the new input method to be switched to. * @param subtype The new subtype of the new input method to be switched to. * @deprecated Use - * {@link InputMethodService#setInputMethodAndSubtype(String, InputMethodSubtype)} + * {@link InputMethodService#switchInputMethod(String, InputMethodSubtype)} * instead. This method was intended for IME developers who should be accessing APIs through * the service. APIs in this class are intended for app developers interacting with the IME. */ @@ -2317,22 +2317,22 @@ public final class InputMethodManager { * which allows it to perform this operation on itself. * @return true if the current input method and subtype was successfully switched to the last * used input method and subtype. - * @deprecated Use {@link InputMethodService#switchToLastInputMethod()} instead. This method + * @deprecated Use {@link InputMethodService#switchToPreviousInputMethod()} instead. This method * was intended for IME developers who should be accessing APIs through the service. APIs in * this class are intended for app developers interacting with the IME. */ @Deprecated public boolean switchToLastInputMethod(IBinder imeToken) { - return switchToLastInputMethodInternal(imeToken); + return switchToPreviousInputMethodInternal(imeToken); } /** * @hide */ - public boolean switchToLastInputMethodInternal(IBinder imeToken) { + public boolean switchToPreviousInputMethodInternal(IBinder imeToken) { synchronized (mH) { try { - return mService.switchToLastInputMethod(imeToken); + return mService.switchToPreviousInputMethod(imeToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index 1789edf1e8760e0acadd0e164867c3f125302208..2b335fb09c614cc130cc8d85a92f08d2cdd832fe 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -29,6 +29,8 @@ import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.ITextLinksCallback; import android.service.textclassifier.ITextSelectionCallback; +import com.android.internal.util.Preconditions; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -40,13 +42,16 @@ final class SystemTextClassifier implements TextClassifier { private static final String LOG_TAG = "SystemTextClassifier"; private final ITextClassifierService mManagerService; + private final TextClassificationConstants mSettings; private final TextClassifier mFallback; private final String mPackageName; - SystemTextClassifier(Context context) throws ServiceManager.ServiceNotFoundException { + SystemTextClassifier(Context context, TextClassificationConstants settings) + throws ServiceManager.ServiceNotFoundException { mManagerService = ITextClassifierService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE)); - mFallback = new TextClassifierImpl(context); + mSettings = Preconditions.checkNotNull(settings); + mFallback = new TextClassifierImpl(context, settings); mPackageName = context.getPackageName(); } @@ -108,6 +113,11 @@ final class SystemTextClassifier implements TextClassifier { public TextLinks generateLinks( @NonNull CharSequence text, @Nullable TextLinks.Options options) { Utils.validate(text, false /* allowInMainThread */); + + if (!mSettings.isSmartLinkifyEnabled()) { + return TextClassifier.NO_OP.generateLinks(text, options); + } + try { if (options == null) { options = new TextLinks.Options().setCallingPackageName(mPackageName); diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java similarity index 62% rename from core/java/android/view/textclassifier/TextClassifierConstants.java rename to core/java/android/view/textclassifier/TextClassificationConstants.java index 397473be9d4b40bf6f6dca4f4c5015f346ae74f5..21b56031556cd5652cef591b5d1f25c670a9d710 100644 --- a/core/java/android/view/textclassifier/TextClassifierConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -30,11 +30,17 @@ import java.util.StringJoiner; * This is encoded as a key=value list, separated by commas. Ex: * *
    - * smart_selection_dark_launch              (boolean)
    - * smart_selection_enabled_for_edit_text    (boolean)
    + * smart_linkify_enabled                    (boolean)
    + * system_textclassifier_enabled            (boolean)
    + * model_dark_launch_enabled                (boolean)
    + * smart_selection_enabled                  (boolean)
    + * smart_text_share_enabled                 (boolean)
    + * smart_linkify_enabled                    (boolean)
    + * smart_select_animation_enabled           (boolean)
      * suggest_selection_max_range_length       (int)
      * classify_text_max_range_length           (int)
      * generate_links_max_text_length           (int)
    + * generate_links_log_sample_rate           (int)
      * entity_list_default                      (String[])
      * entity_list_not_editable                 (String[])
      * entity_list_editable                     (String[])
    @@ -46,20 +52,28 @@ import java.util.StringJoiner;
      *
      * Example of setting the values for testing.
      * adb shell settings put global text_classifier_constants \
    - *      smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true,\
    + *      model_dark_launch_enabled=true,smart_selection_enabled=true,\
      *      entity_list_default=phone:address
      * @hide
      */
    -public final class TextClassifierConstants {
    -
    -    private static final String LOG_TAG = "TextClassifierConstants";
    -
    -    private static final String SMART_SELECTION_DARK_LAUNCH =
    -            "smart_selection_dark_launch";
    -    private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
    -            "smart_selection_enabled_for_edit_text";
    +public final class TextClassificationConstants {
    +
    +    private static final String LOG_TAG = "TextClassificationConstants";
    +
    +    private static final String LOCAL_TEXT_CLASSIFIER_ENABLED =
    +            "local_textclassifier_enabled";
    +    private static final String SYSTEM_TEXT_CLASSIFIER_ENABLED =
    +            "system_textclassifier_enabled";
    +    private static final String MODEL_DARK_LAUNCH_ENABLED =
    +            "model_dark_launch_enabled";
    +    private static final String SMART_SELECTION_ENABLED =
    +            "smart_selection_enabled";
    +    private static final String SMART_TEXT_SHARE_ENABLED =
    +            "smart_text_share_enabled";
         private static final String SMART_LINKIFY_ENABLED =
                 "smart_linkify_enabled";
    +    private static final String SMART_SELECT_ANIMATION_ENABLED =
    +            "smart_select_animation_enabled";
         private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
                 "suggest_selection_max_range_length";
         private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
    @@ -75,9 +89,13 @@ public final class TextClassifierConstants {
         private static final String ENTITY_LIST_EDITABLE =
                 "entity_list_editable";
     
    -    private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
    -    private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
    +    private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
    +    private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
    +    private static final boolean MODEL_DARK_LAUNCH_ENABLED_DEFAULT = false;
    +    private static final boolean SMART_SELECTION_ENABLED_DEFAULT = true;
    +    private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
         private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
    +    private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
         private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
         private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
         private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
    @@ -92,12 +110,13 @@ public final class TextClassifierConstants {
                 .add(TextClassifier.TYPE_DATE_TIME)
                 .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
     
    -    /** Default settings. */
    -    static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
    -
    -    private final boolean mDarkLaunch;
    -    private final boolean mSuggestSelectionEnabledForEditableText;
    +    private final boolean mSystemTextClassifierEnabled;
    +    private final boolean mLocalTextClassifierEnabled;
    +    private final boolean mModelDarkLaunchEnabled;
    +    private final boolean mSmartSelectionEnabled;
    +    private final boolean mSmartTextShareEnabled;
         private final boolean mSmartLinkifyEnabled;
    +    private final boolean mSmartSelectionAnimationEnabled;
         private final int mSuggestSelectionMaxRangeLength;
         private final int mClassifyTextMaxRangeLength;
         private final int mGenerateLinksMaxTextLength;
    @@ -106,20 +125,7 @@ public final class TextClassifierConstants {
         private final List mEntityListNotEditable;
         private final List mEntityListEditable;
     
    -    private TextClassifierConstants() {
    -        mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
    -        mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
    -        mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
    -        mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
    -        mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
    -        mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
    -        mGenerateLinksLogSampleRate = GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT;
    -        mEntityListDefault = parseEntityList(ENTITY_LIST_DEFAULT_VALUE);
    -        mEntityListNotEditable = mEntityListDefault;
    -        mEntityListEditable = mEntityListDefault;
    -    }
    -
    -    private TextClassifierConstants(@Nullable String settings) {
    +    private TextClassificationConstants(@Nullable String settings) {
             final KeyValueListParser parser = new KeyValueListParser(',');
             try {
                 parser.setString(settings);
    @@ -127,15 +133,27 @@ public final class TextClassifierConstants {
                 // Failed to parse the settings string, log this and move on with defaults.
                 Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
             }
    -        mDarkLaunch = parser.getBoolean(
    -                SMART_SELECTION_DARK_LAUNCH,
    -                SMART_SELECTION_DARK_LAUNCH_DEFAULT);
    -        mSuggestSelectionEnabledForEditableText = parser.getBoolean(
    -                SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
    -                SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
    +        mSystemTextClassifierEnabled = parser.getBoolean(
    +                SYSTEM_TEXT_CLASSIFIER_ENABLED,
    +                SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
    +        mLocalTextClassifierEnabled = parser.getBoolean(
    +                LOCAL_TEXT_CLASSIFIER_ENABLED,
    +                LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
    +        mModelDarkLaunchEnabled = parser.getBoolean(
    +                MODEL_DARK_LAUNCH_ENABLED,
    +                MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
    +        mSmartSelectionEnabled = parser.getBoolean(
    +                SMART_SELECTION_ENABLED,
    +                SMART_SELECTION_ENABLED_DEFAULT);
    +        mSmartTextShareEnabled = parser.getBoolean(
    +                SMART_TEXT_SHARE_ENABLED,
    +                SMART_TEXT_SHARE_ENABLED_DEFAULT);
             mSmartLinkifyEnabled = parser.getBoolean(
                     SMART_LINKIFY_ENABLED,
                     SMART_LINKIFY_ENABLED_DEFAULT);
    +        mSmartSelectionAnimationEnabled = parser.getBoolean(
    +                SMART_SELECT_ANIMATION_ENABLED,
    +                SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
             mSuggestSelectionMaxRangeLength = parser.getInt(
                     SUGGEST_SELECTION_MAX_RANGE_LENGTH,
                     SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
    @@ -160,22 +178,38 @@ public final class TextClassifierConstants {
         }
     
         /** Load from a settings string. */
    -    public static TextClassifierConstants loadFromString(String settings) {
    -        return new TextClassifierConstants(settings);
    +    public static TextClassificationConstants loadFromString(String settings) {
    +        return new TextClassificationConstants(settings);
    +    }
    +
    +    public boolean isLocalTextClassifierEnabled() {
    +        return mLocalTextClassifierEnabled;
         }
     
    -    public boolean isDarkLaunch() {
    -        return mDarkLaunch;
    +    public boolean isSystemTextClassifierEnabled() {
    +        return mSystemTextClassifierEnabled;
         }
     
    -    public boolean isSuggestSelectionEnabledForEditableText() {
    -        return mSuggestSelectionEnabledForEditableText;
    +    public boolean isModelDarkLaunchEnabled() {
    +        return mModelDarkLaunchEnabled;
    +    }
    +
    +    public boolean isSmartSelectionEnabled() {
    +        return mSmartSelectionEnabled;
    +    }
    +
    +    public boolean isSmartTextShareEnabled() {
    +        return mSmartTextShareEnabled;
         }
     
         public boolean isSmartLinkifyEnabled() {
             return mSmartLinkifyEnabled;
         }
     
    +    public boolean isSmartSelectionAnimationEnabled() {
    +        return mSmartSelectionAnimationEnabled;
    +    }
    +
         public int getSuggestSelectionMaxRangeLength() {
             return mSuggestSelectionMaxRangeLength;
         }
    diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
    index 300aef2d172e46c1137948aa6d7006502a2ee5f7..a7f1ca1aa2391590ce3e689959a3289fcfb81ebd 100644
    --- a/core/java/android/view/textclassifier/TextClassificationManager.java
    +++ b/core/java/android/view/textclassifier/TextClassificationManager.java
    @@ -20,8 +20,11 @@ import android.annotation.Nullable;
     import android.annotation.SystemService;
     import android.content.Context;
     import android.os.ServiceManager;
    +import android.provider.Settings;
     import android.service.textclassifier.TextClassifierService;
    +import android.view.textclassifier.TextClassifier.TextClassifierType;
     
    +import com.android.internal.annotations.GuardedBy;
     import com.android.internal.util.Preconditions;
     
     /**
    @@ -30,55 +33,41 @@ import com.android.internal.util.Preconditions;
     @SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
     public final class TextClassificationManager {
     
    -    // TODO: Make this a configurable flag.
    -    private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED = true;
    -
         private static final String LOG_TAG = "TextClassificationManager";
     
         private final Object mLock = new Object();
     
         private final Context mContext;
    +    private final TextClassificationConstants mSettings;
    +
    +    @GuardedBy("mLock")
         private TextClassifier mTextClassifier;
    +    @GuardedBy("mLock")
    +    private TextClassifier mLocalTextClassifier;
    +    @GuardedBy("mLock")
         private TextClassifier mSystemTextClassifier;
     
         /** @hide */
         public TextClassificationManager(Context context) {
             mContext = Preconditions.checkNotNull(context);
    +        mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
    +                context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
         }
     
         /**
    -     * Returns the system's default TextClassifier.
    -     * @hide
    -     */
    -    // TODO: Unhide when this is ready.
    -    public TextClassifier getSystemDefaultTextClassifier() {
    -        synchronized (mLock) {
    -            if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
    -                try {
    -                    Log.d(LOG_TAG, "Initialized SystemTextClassifier");
    -                    mSystemTextClassifier = new SystemTextClassifier(mContext);
    -                } catch (ServiceManager.ServiceNotFoundException e) {
    -                    Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
    -                }
    -            }
    -            if (mSystemTextClassifier == null) {
    -                Log.d(LOG_TAG, "Using an in-process TextClassifier as the system default");
    -                mSystemTextClassifier = new TextClassifierImpl(mContext);
    -            }
    -        }
    -        return mSystemTextClassifier;
    -    }
    -
    -    /**
    -     * Returns the text classifier.
    +     * Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}.
    +     * If this is null, this method returns a default text classifier (i.e. either the system text
    +     * classifier if one exists, or a local text classifier running in this app.)
    +     *
    +     * @see #setTextClassifier(TextClassifier)
          */
         public TextClassifier getTextClassifier() {
             synchronized (mLock) {
                 if (mTextClassifier == null) {
                     if (isSystemTextClassifierEnabled()) {
    -                    mTextClassifier = getSystemDefaultTextClassifier();
    +                    mTextClassifier = getSystemTextClassifier();
                     } else {
    -                    mTextClassifier = new TextClassifierImpl(mContext);
    +                    mTextClassifier = getLocalTextClassifier();
                     }
                 }
                 return mTextClassifier;
    @@ -96,8 +85,75 @@ public final class TextClassificationManager {
             }
         }
     
    +    /**
    +     * Returns a specific type of text classifier.
    +     * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
    +     *
    +     * @see TextClassifier#LOCAL
    +     * @see TextClassifier#SYSTEM
    +     * @hide
    +     */
    +    // TODO: Expose as system API.
    +    public TextClassifier getTextClassifier(@TextClassifierType int type) {
    +        switch (type) {
    +            case TextClassifier.LOCAL:
    +                return getLocalTextClassifier();
    +            default:
    +                return getSystemTextClassifier();
    +        }
    +    }
    +
    +    /** @hide */
    +    public TextClassificationConstants getSettings() {
    +        return mSettings;
    +    }
    +
    +    private TextClassifier getSystemTextClassifier() {
    +        synchronized (mLock) {
    +            if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
    +                try {
    +                    mSystemTextClassifier = new SystemTextClassifier(mContext, mSettings);
    +                    Log.d(LOG_TAG, "Initialized SystemTextClassifier");
    +                } catch (ServiceManager.ServiceNotFoundException e) {
    +                    Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
    +                }
    +            }
    +        }
    +        if (mSystemTextClassifier != null) {
    +            return mSystemTextClassifier;
    +        }
    +        return TextClassifier.NO_OP;
    +    }
    +
    +    private TextClassifier getLocalTextClassifier() {
    +        synchronized (mLock) {
    +            if (mLocalTextClassifier == null) {
    +                if (mSettings.isLocalTextClassifierEnabled()) {
    +                    mLocalTextClassifier = new TextClassifierImpl(mContext, mSettings);
    +                } else {
    +                    Log.d(LOG_TAG, "Local TextClassifier disabled");
    +                    mLocalTextClassifier = TextClassifierImpl.NO_OP;
    +                }
    +            }
    +            return mLocalTextClassifier;
    +        }
    +    }
    +
         private boolean isSystemTextClassifierEnabled() {
    -        return SYSTEM_TEXT_CLASSIFIER_ENABLED
    +        return mSettings.isSystemTextClassifierEnabled()
                     && TextClassifierService.getServiceComponentName(mContext) != null;
         }
    +
    +    /** @hide */
    +    public static TextClassificationConstants getSettings(Context context) {
    +        Preconditions.checkNotNull(context);
    +        final TextClassificationManager tcm =
    +                context.getSystemService(TextClassificationManager.class);
    +        if (tcm != null) {
    +            return tcm.mSettings;
    +        } else {
    +            return TextClassificationConstants.loadFromString(Settings.Global.getString(
    +                    context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
    +        }
    +    }
     }
    diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
    index d52a30bcc018db325076a7e97cc2260f71397570..ec40fdd0ffb1c03161cb04bdc795b81c3db312b3 100644
    --- a/core/java/android/view/textclassifier/TextClassifier.java
    +++ b/core/java/android/view/textclassifier/TextClassifier.java
    @@ -16,6 +16,7 @@
     
     package android.view.textclassifier;
     
    +import android.annotation.IntDef;
     import android.annotation.IntRange;
     import android.annotation.NonNull;
     import android.annotation.Nullable;
    @@ -49,6 +50,16 @@ public interface TextClassifier {
         /** @hide */
         String DEFAULT_LOG_TAG = "androidtc";
     
    +
    +    /** @hide */
    +    @Retention(RetentionPolicy.SOURCE)
    +    @IntDef(value = {LOCAL, SYSTEM})
    +    @interface TextClassifierType {}  // TODO: Expose as system APIs.
    +    /** Specifies a TextClassifier that runs locally in the app's process. @hide */
    +    int LOCAL = 0;
    +    /** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
    +    int SYSTEM = 1;
    +
         /** The TextClassifier failed to run. */
         String TYPE_UNKNOWN = "";
         /** The classifier ran, but didn't recognize a known entity. */
    @@ -328,14 +339,6 @@ public interface TextClassifier {
             return Logger.DISABLED;
         }
     
    -    /**
    -     * Returns this TextClassifier's settings.
    -     * @hide
    -     */
    -    default TextClassifierConstants getSettings() {
    -        return TextClassifierConstants.DEFAULT;
    -    }
    -
         /**
          * Configuration object for specifying what entities to identify.
          *
    diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
    index 5b7095b2f44faf4955096aa8c5412b788d0404ba..41f1c69a47edc1cc875cb2b10cce03cd23961d0b 100644
    --- a/core/java/android/view/textclassifier/TextClassifierImpl.java
    +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
    @@ -34,7 +34,6 @@ import android.os.UserManager;
     import android.provider.Browser;
     import android.provider.CalendarContract;
     import android.provider.ContactsContract;
    -import android.provider.Settings;
     import android.view.textclassifier.logging.DefaultLogger;
     import android.view.textclassifier.logging.GenerateLinksLogger;
     import android.view.textclassifier.logging.Logger;
    @@ -99,13 +98,13 @@ public final class TextClassifierImpl implements TextClassifier {
         @GuardedBy("mLoggerLock") // Do not access outside this lock.
         private Logger mLogger;  // Should never be null if mLoggerConfig.get() is not null.
     
    -    private TextClassifierConstants mSettings;
    +    private final TextClassificationConstants mSettings;
     
    -    public TextClassifierImpl(Context context) {
    +    public TextClassifierImpl(Context context, TextClassificationConstants settings) {
             mContext = Preconditions.checkNotNull(context);
             mFallback = TextClassifier.NO_OP;
    -        mGenerateLinksLogger = new GenerateLinksLogger(
    -                getSettings().getGenerateLinksLogSampleRate());
    +        mSettings = Preconditions.checkNotNull(settings);
    +        mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
         }
     
         /** @inheritDoc */
    @@ -117,7 +116,7 @@ public final class TextClassifierImpl implements TextClassifier {
             try {
                 final int rangeLength = selectionEndIndex - selectionStartIndex;
                 if (text.length() > 0
    -                    && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
    +                    && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
                     final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
                     final String localesString = concatenateLocales(locales);
                     final Calendar refTime = Calendar.getInstance();
    @@ -126,7 +125,7 @@ public final class TextClassifierImpl implements TextClassifier {
                     final String string = text.toString();
                     final int start;
                     final int end;
    -                if (getSettings().isDarkLaunch() && !darkLaunchAllowed) {
    +                if (mSettings.isModelDarkLaunchEnabled() && !darkLaunchAllowed) {
                         start = selectionStartIndex;
                         end = selectionEndIndex;
                     } else {
    @@ -179,7 +178,7 @@ public final class TextClassifierImpl implements TextClassifier {
             Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
             try {
                 final int rangeLength = endIndex - startIndex;
    -            if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
    +            if (text.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
                     final String string = text.toString();
                     final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
                     final String localesString = concatenateLocales(locales);
    @@ -214,7 +213,7 @@ public final class TextClassifierImpl implements TextClassifier {
             final String textString = text.toString();
             final TextLinks.Builder builder = new TextLinks.Builder(textString);
     
    -        if (!getSettings().isSmartLinkifyEnabled()) {
    +        if (!mSettings.isSmartLinkifyEnabled()) {
                 return builder.build();
             }
     
    @@ -226,7 +225,7 @@ public final class TextClassifierImpl implements TextClassifier {
                         options != null && options.getEntityConfig() != null
                                 ? options.getEntityConfig().resolveEntityListModifications(
                                         getEntitiesForHints(options.getEntityConfig().getHints()))
    -                            : getSettings().getEntityListDefault();
    +                            : mSettings.getEntityListDefault();
                 final TextClassifierImplNative nativeImpl =
                         getNative(defaultLocales);
                 final TextClassifierImplNative.AnnotatedSpan[] annotations =
    @@ -268,7 +267,7 @@ public final class TextClassifierImpl implements TextClassifier {
         /** @inheritDoc */
         @Override
         public int getMaxGenerateLinksTextLength() {
    -        return getSettings().getGenerateLinksMaxTextLength();
    +        return mSettings.getGenerateLinksMaxTextLength();
         }
     
         private Collection getEntitiesForHints(Collection hints) {
    @@ -278,11 +277,11 @@ public final class TextClassifierImpl implements TextClassifier {
             // Use the default if there is no hint, or conflicting ones.
             final boolean useDefault = editable == notEditable;
             if (useDefault) {
    -            return getSettings().getEntityListDefault();
    +            return mSettings.getEntityListDefault();
             } else if (editable) {
    -            return getSettings().getEntityListEditable();
    +            return mSettings.getEntityListEditable();
             } else {  // notEditable
    -            return getSettings().getEntityListNotEditable();
    +            return mSettings.getEntityListNotEditable();
             }
         }
     
    @@ -298,16 +297,6 @@ public final class TextClassifierImpl implements TextClassifier {
             }
         }
     
    -    /** @hide */
    -    @Override
    -    public TextClassifierConstants getSettings() {
    -        if (mSettings == null) {
    -            mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
    -                    mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
    -        }
    -        return mSettings;
    -    }
    -
         private TextClassifierImplNative getNative(LocaleList localeList)
                 throws FileNotFoundException {
             synchronized (mLock) {
    diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
    index 884cbe861debf8bf0d2f00328f19337e3eacf2c0..af9fc7d16655b7d59971d2e7663ec5db2e04f220 100644
    --- a/core/java/android/view/textclassifier/TextLinks.java
    +++ b/core/java/android/view/textclassifier/TextLinks.java
    @@ -505,7 +505,7 @@ public final class TextLinks implements Parcelable {
             public void onClick(View widget) {
                 if (widget instanceof TextView) {
                     final TextView textView = (TextView) widget;
    -                textView.requestActionMode(mTextLink);
    +                textView.requestActionMode(this);
                 }
             }
     
    diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
    index f368c74a17b58cfbc0d60361c514d747c86eac42..21ec42b1d55748228f44860e7347325664133988 100644
    --- a/core/java/android/view/textservice/TextServicesManager.java
    +++ b/core/java/android/view/textservice/TextServicesManager.java
    @@ -66,6 +66,12 @@ public final class TextServicesManager {
         private static final String TAG = TextServicesManager.class.getSimpleName();
         private static final boolean DBG = false;
     
    +    /**
    +     * A compile time switch to control per-profile spell checker, which is not yet ready.
    +     * @hide
    +     */
    +    public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true;
    +
         private static TextServicesManager sInstance;
     
         private final ITextServicesManager mService;
    diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
    index 84c000a379dfce0aeff4972ef8b11a49d0d41d40..ed122a650e66cd84a7b27dd158c9ce4161349593 100644
    --- a/core/java/android/webkit/URLUtil.java
    +++ b/core/java/android/webkit/URLUtil.java
    @@ -39,7 +39,7 @@ public final class URLUtil {
         // "file:///android_res/drawable/bar.png". Use "drawable" to refer to
         // "drawable-hdpi" directory as well.
         static final String RESOURCE_BASE = "file:///android_res/";
    -    static final String FILE_BASE = "file://";
    +    static final String FILE_BASE = "file:";
         static final String PROXY_BASE = "file:///cookieless_proxy/";
         static final String CONTENT_BASE = "content:";
     
    diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
    index a8f6b03cd97a76e8e92e4d48141f6997ee734775..5d3f1c9288cd0479b2e3eab435b43d40f8c134dc 100644
    --- a/core/java/android/webkit/WebView.java
    +++ b/core/java/android/webkit/WebView.java
    @@ -1683,7 +1683,7 @@ public class WebView extends AbsoluteLayout
          * @param callback will be called on the UI thread with {@code true} if initialization is
          * successful, {@code false} otherwise.
          */
    -    public static void startSafeBrowsing(Context context,
    +    public static void startSafeBrowsing(@NonNull Context context,
                 @Nullable ValueCallback callback) {
             getFactory().getStatics().initSafeBrowsing(context, callback);
         }
    @@ -3121,6 +3121,11 @@ public class WebView extends AbsoluteLayout
             mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
         }
     
    +    @Override
    +    public boolean onCheckIsTextEditor() {
    +        return mProvider.getViewDelegate().onCheckIsTextEditor();
    +    }
    +
         /** @hide */
         @Override
         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
    diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
    index e9fe481112a20f6736ebdf7435c2814926078b6c..e0ccda98bf1d539a2d2ee05b490ff2d2b4e4f7b2 100644
    --- a/core/java/android/webkit/WebViewFactory.java
    +++ b/core/java/android/webkit/WebViewFactory.java
    @@ -205,25 +205,21 @@ public final class WebViewFactory {
             }
     
             PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager();
    -        PackageInfo packageInfo;
    +        String libraryFileName;
             try {
    -            packageInfo = packageManager.getPackageInfo(packageName,
    +            PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
                         PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
    +            libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
             } catch (PackageManager.NameNotFoundException e) {
                 Log.e(LOGTAG, "Couldn't find package " + packageName);
                 return LIBLOAD_WRONG_PACKAGE_NAME;
             }
     
    -        try {
    -            int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, packageInfo);
    -            // If we failed waiting for relro we want to return that fact even if we successfully
    -            // load the relro file.
    -            if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
    -            return loadNativeRet;
    -        } catch (MissingWebViewPackageException e) {
    -            Log.e(LOGTAG, "Couldn't load native library: " + e);
    -            return LIBLOAD_FAILED_TO_LOAD_LIBRARY;
    -        }
    +        int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, libraryFileName);
    +        // If we failed waiting for relro we want to return that fact even if we successfully
    +        // load the relro file.
    +        if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
    +        return loadNativeRet;
         }
     
         static WebViewFactoryProvider getProvider() {
    @@ -454,7 +450,8 @@ public final class WebViewFactory {
                     ClassLoader clazzLoader = webViewContext.getClassLoader();
     
                     Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
    -                WebViewLibraryLoader.loadNativeLibrary(clazzLoader, sPackageInfo);
    +                WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
    +                        getWebViewLibrary(sPackageInfo.applicationInfo));
                     Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
     
                     Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
    diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
    index eb2b6bccf4ccca518e438634a7f1109c11ee5e1d..cabba06bdff5f07598108b5b7ad478551cb9eaf0 100644
    --- a/core/java/android/webkit/WebViewLibraryLoader.java
    +++ b/core/java/android/webkit/WebViewLibraryLoader.java
    @@ -234,17 +234,14 @@ public class WebViewLibraryLoader {
          * 

    Note: Assumes that we have waited for relro creation. * * @param clazzLoader class loader used to find the linker namespace to load the library into. - * @param packageInfo the package from which WebView is loaded. + * @param libraryFileName the filename of the library to load. */ - static int loadNativeLibrary(ClassLoader clazzLoader, PackageInfo packageInfo) - throws WebViewFactory.MissingWebViewPackageException { + public static int loadNativeLibrary(ClassLoader clazzLoader, String libraryFileName) { if (!sAddressSpaceReserved) { Log.e(LOGTAG, "can't load with relro file; address space not reserved"); return WebViewFactory.LIBLOAD_ADDRESS_SPACE_NOT_RESERVED; } - final String libraryFileName = - WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo); String relroPath = VMRuntime.getRuntime().is64Bit() ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 : CHROMIUM_WEBVIEW_NATIVE_RELRO_32; int result = nativeLoadWithRelroFile(libraryFileName, relroPath, clazzLoader); diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index a8969252ff2ed887f04d6f9cad465b6357b76524..a474a8580b694b2d2451562b93b8f2727a2cd26e 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -424,6 +424,11 @@ public interface WebViewProvider { public Handler getHandler(Handler originalHandler); public View findFocus(View originalFocusedView); + + @SuppressWarnings("unused") + default boolean onCheckIsTextEditor() { + return false; + } } interface ScrollDelegate { diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index 63fbef3f4bc403624651b566e91e4c5a683ecc40..07593a5122160406a7db336ac96cc5905344fe1f 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -93,13 +93,11 @@ public class WebViewZygote { synchronized (sLock) { sMultiprocessEnabled = enabled; - // When toggling between multi-process being on/off, start or stop the - // zygote. If it is enabled and the zygote is not yet started, launch it. - // Otherwise, kill it. The name may be null if the package information has - // not yet been resolved. - if (enabled) { - connectToZygoteIfNeededLocked(); - } else { + // When multi-process is disabled, kill the zygote. When it is enabled, + // the zygote is not explicitly started here to avoid waiting on the + // zygote launch at boot. Instead, the zygote will be started when it is + // first needed in getProcess(). + if (!enabled) { stopZygoteLocked(); } } @@ -169,6 +167,8 @@ public class WebViewZygote { final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : TextUtils.join(File.pathSeparator, zipPaths); + String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo); + // In the case where the ApplicationInfo has been modified by the stub WebView, // we need to use the original ApplicationInfo to determine what the original classpath // would have been to use as a cache key. @@ -179,7 +179,7 @@ public class WebViewZygote { ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress()); Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath); - sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey, + sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey, Build.SUPPORTED_ABIS[0]); } catch (Exception e) { Log.e(LOGTAG, "Error connecting to webview zygote", e); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index a13de75edee8682cff361cc48bcbe01417943efb..1360af2b41c6de26be72a155d2b88615960bcb36 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -31,10 +31,10 @@ import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.os.Debug; import android.os.Handler; -import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.os.StrictMode; +import android.os.SystemProperties; import android.os.Trace; import android.text.Editable; import android.text.InputType; @@ -111,6 +111,10 @@ public abstract class AbsListView extends AdapterView implements Te @SuppressWarnings("UnusedDeclaration") private static final String TAG = "AbsListView"; + private static final boolean OPTS_INPUT = SystemProperties.getBoolean("persist.vendor.qti.inputopts.enable",false); + private static final String MOVE_TOUCH_SLOP = SystemProperties.get("persist.vendor.qti.inputopts.movetouchslop","0.6"); + private static final double TOUCH_SLOP_MIN = 0.6; + private static final double TOUCH_SLOP_MAX = 1.0; /** * Disables the transcript mode. @@ -739,6 +743,10 @@ public abstract class AbsListView extends AdapterView implements Te */ private boolean mIsDetaching; + private boolean mIsFirstTouchMoveEvent = false; + private int mMoveAcceleration; + private int mNumTouchMoveEvent = 0; + /** * Interface definition for a callback to be invoked when the list or grid * has been scrolled. @@ -880,6 +888,20 @@ public abstract class AbsListView extends AdapterView implements Te final ViewConfiguration configuration = ViewConfiguration.get(mContext); mTouchSlop = configuration.getScaledTouchSlop(); mVerticalScrollFactor = configuration.getScaledVerticalScrollFactor(); + if (OPTS_INPUT) { + double touchslopprop = Double.parseDouble(MOVE_TOUCH_SLOP); + if (touchslopprop > 0) { + if (touchslopprop < TOUCH_SLOP_MIN) { + mMoveAcceleration = (int)(mTouchSlop * TOUCH_SLOP_MIN); + } else if ((touchslopprop >= TOUCH_SLOP_MIN) && (touchslopprop < TOUCH_SLOP_MAX)){ + mMoveAcceleration = (int)(mTouchSlop * touchslopprop); + } else { + mMoveAcceleration = mTouchSlop; + } + } else { + mMoveAcceleration = mTouchSlop; + } + } mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mOverscrollDistance = configuration.getScaledOverscrollDistance(); @@ -3486,7 +3508,18 @@ public abstract class AbsListView extends AdapterView implements Te final int deltaY = y - mMotionY; final int distance = Math.abs(deltaY); final boolean overscroll = mScrollY != 0; - if ((overscroll || distance > mTouchSlop) && + boolean isFarEnough = false; + if (OPTS_INPUT) { + if (mIsFirstTouchMoveEvent) { + isFarEnough = distance > mMoveAcceleration; + } else { + isFarEnough = distance > mTouchSlop; + } + } else { + isFarEnough = distance > mTouchSlop; + } + + if ((overscroll || isFarEnough) && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) { createScrollingCache(); if (overscroll) { @@ -3494,7 +3527,11 @@ public abstract class AbsListView extends AdapterView implements Te mMotionCorrection = 0; } else { mTouchMode = TOUCH_MODE_SCROLL; - mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop; + if (mIsFirstTouchMoveEvent) { + mMotionCorrection = deltaY > 0 ? mMoveAcceleration : -mMoveAcceleration; + } else { + mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop; + } } removeCallbacks(mPendingCheckForLongPress); setPressed(false); @@ -3811,21 +3848,38 @@ public abstract class AbsListView extends AdapterView implements Te switch (actionMasked) { case MotionEvent.ACTION_DOWN: { onTouchDown(ev); + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } break; } case MotionEvent.ACTION_MOVE: { + if (OPTS_INPUT) { + mNumTouchMoveEvent++; + if (mNumTouchMoveEvent == 1) { + mIsFirstTouchMoveEvent = true; + } else { + mIsFirstTouchMoveEvent = false; + } + } onTouchMove(ev, vtev); break; } case MotionEvent.ACTION_UP: { onTouchUp(ev); + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } break; } case MotionEvent.ACTION_CANCEL: { onTouchCancel(); + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } break; } @@ -3841,6 +3895,9 @@ public abstract class AbsListView extends AdapterView implements Te mMotionPosition = motionPosition; } mLastY = y; + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } break; } @@ -3862,6 +3919,9 @@ public abstract class AbsListView extends AdapterView implements Te mMotionPosition = motionPosition; } mLastY = y; + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } break; } } @@ -4039,6 +4099,11 @@ public abstract class AbsListView extends AdapterView implements Te } mSelector.setHotspot(x, ev.getY()); } + if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) { + if (!post(performClick)) { + performClick.run(); + } + } if (mTouchModeReset != null) { removeCallbacks(mTouchModeReset); } @@ -4049,9 +4114,6 @@ public abstract class AbsListView extends AdapterView implements Te mTouchMode = TOUCH_MODE_REST; child.setPressed(false); setPressed(false); - if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) { - performClick.run(); - } } }; postDelayed(mTouchModeReset, @@ -4440,6 +4502,9 @@ public abstract class AbsListView extends AdapterView implements Te switch (actionMasked) { case MotionEvent.ACTION_DOWN: { + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } int touchMode = mTouchMode; if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { mMotionCorrection = 0; @@ -4474,6 +4539,14 @@ public abstract class AbsListView extends AdapterView implements Te } case MotionEvent.ACTION_MOVE: { + if (OPTS_INPUT) { + mNumTouchMoveEvent++; + if (mNumTouchMoveEvent == 1) { + mIsFirstTouchMoveEvent = true; + } else { + mIsFirstTouchMoveEvent = false; + } + } switch (mTouchMode) { case TOUCH_MODE_DOWN: int pointerIndex = ev.findPointerIndex(mActivePointerId); @@ -4494,6 +4567,9 @@ public abstract class AbsListView extends AdapterView implements Te case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } mTouchMode = TOUCH_MODE_REST; mActivePointerId = INVALID_POINTER; recycleVelocityTracker(); @@ -4503,6 +4579,9 @@ public abstract class AbsListView extends AdapterView implements Te } case MotionEvent.ACTION_POINTER_UP: { + if (OPTS_INPUT) { + mNumTouchMoveEvent = 0; + } onSecondaryPointerUp(ev); break; } @@ -6036,11 +6115,6 @@ public abstract class AbsListView extends AdapterView implements Te public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { return getTarget().commitContent(inputContentInfo, flags, opts); } - - @Override - public void reportLanguageHint(@NonNull LocaleList languageHint) { - getTarget().reportLanguageHint(languageHint); - } } /** diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 1d1fcc969b56a0215419ea4cc84cf221336e4c9a..61a58733479f3c05e0bb416bfe74edea9d3d8beb 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -863,7 +863,7 @@ public abstract class AbsSeekBar extends ProgressBar { } final int range = getMax() - getMin(); - progress += scale * range; + progress += scale * range + getMin(); setHotspot(x, y); setProgressInternal(Math.round(progress), true, false); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 2e7b2fd65d33f7ddcb5fac78903c50b805084323..27dd39b3224f809ad33a7c40fd75c80029ae6667 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -37,6 +37,7 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; @@ -107,7 +108,7 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassification; -import android.view.textclassifier.TextLinks; +import android.view.textclassifier.TextClassificationManager; import android.widget.AdapterView.OnItemClickListener; import android.widget.TextView.Drawables; import android.widget.TextView.OnEditorActionListener; @@ -2117,13 +2118,12 @@ public class Editor { getSelectionActionModeHelper().startSelectionActionModeAsync(adjustSelection); } - void startLinkActionModeAsync(TextLinks.TextLink link) { - Preconditions.checkNotNull(link); + void startLinkActionModeAsync(int start, int end) { if (!(mTextView.getText() instanceof Spannable)) { return; } stopTextActionMode(); - getSelectionActionModeHelper().startLinkActionModeAsync(link); + getSelectionActionModeHelper().startLinkActionModeAsync(start, end); } /** @@ -4024,7 +4024,7 @@ public class Editor { private void updateAssistMenuItems(Menu menu) { clearAssistMenuItems(menu); - if (!mTextView.isDeviceProvisioned()) { + if (!shouldEnableAssistMenuItems()) { return; } final TextClassification textClassification = @@ -4097,7 +4097,7 @@ public class Editor { final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); - if (!mTextView.isDeviceProvisioned() || textClassification == null) { + if (!shouldEnableAssistMenuItems() || textClassification == null) { // No textClassification result to handle the click. Eat the click. return true; } @@ -4118,6 +4118,12 @@ public class Editor { return true; } + private boolean shouldEnableAssistMenuItems() { + return mTextView.isDeviceProvisioned() + && TextClassificationManager.getSettings(mTextView.getContext()) + .isSmartTextShareEnabled(); + } + @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { getSelectionActionModeHelper().onSelectionAction(item.getItemId()); @@ -4643,10 +4649,14 @@ public class Editor { return 0; } - protected final void showMagnifier(@NonNull final MotionEvent event) { - if (mMagnifier == null) { - return; - } + /** + * Computes the position where the magnifier should be shown, relative to + * {@code mTextView}, and writes them to {@code showPosInView}. Also decides + * whether the magnifier should be shown or dismissed after this touch event. + * @return Whether the magnifier should be shown at the computed coordinates or dismissed. + */ + private boolean obtainMagnifierShowCoordinates(@NonNull final MotionEvent event, + final PointF showPosInView) { final int trigger = getMagnifierHandleTrigger(); final int offset; @@ -4664,26 +4674,52 @@ public class Editor { } if (offset == -1) { - dismissMagnifier(); + return false; } final Layout layout = mTextView.getLayout(); final int lineNumber = layout.getLineForOffset(offset); - // Horizontally move the magnifier smoothly. + + // Horizontally move the magnifier smoothly but clamp inside the current line. final int[] textViewLocationOnScreen = new int[2]; mTextView.getLocationOnScreen(textViewLocationOnScreen); - final float xPosInView = event.getRawX() - textViewLocationOnScreen[0]; + final float touchXInView = event.getRawX() - textViewLocationOnScreen[0]; + final float lineLeft = mTextView.getLayout().getLineLeft(lineNumber) + + mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); + final float lineRight = mTextView.getLayout().getLineRight(lineNumber) + + mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); + final float contentWidth = Math.round(mMagnifier.getWidth() / mMagnifier.getZoom()); + if (touchXInView < lineLeft - contentWidth / 2 + || touchXInView > lineRight + contentWidth / 2) { + // The touch is too out of the bounds of the current line, so hide the magnifier. + return false; + } + showPosInView.x = Math.max(lineLeft, Math.min(lineRight, touchXInView)); + // Vertically snap to middle of current line. - final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber) + showPosInView.y = (mTextView.getLayout().getLineTop(lineNumber) + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f + mTextView.getTotalPaddingTop() - mTextView.getScrollY(); - // Make the cursor visible and stop blinking. - mRenderCursorRegardlessTiming = true; - mTextView.invalidateCursorPath(); - suspendBlink(); + return true; + } - mMagnifier.show(xPosInView, yPosInView); + protected final void updateMagnifier(@NonNull final MotionEvent event) { + if (mMagnifier == null) { + return; + } + + final PointF showPosInView = new PointF(); + final boolean shouldShow = obtainMagnifierShowCoordinates(event, showPosInView); + if (shouldShow) { + // Make the cursor visible and stop blinking. + mRenderCursorRegardlessTiming = true; + mTextView.invalidateCursorPath(); + suspendBlink(); + mMagnifier.show(showPosInView.x, showPosInView.y); + } else { + dismissMagnifier(); + } } protected final void dismissMagnifier() { @@ -4872,11 +4908,11 @@ public class Editor { case MotionEvent.ACTION_DOWN: mDownPositionX = ev.getRawX(); mDownPositionY = ev.getRawY(); - showMagnifier(ev); + updateMagnifier(ev); break; case MotionEvent.ACTION_MOVE: - showMagnifier(ev); + updateMagnifier(ev); break; case MotionEvent.ACTION_UP: @@ -5230,11 +5266,11 @@ public class Editor { // re-engages the handle. mTouchWordDelta = 0.0f; mPrevX = UNSET_X_VALUE; - showMagnifier(event); + updateMagnifier(event); break; case MotionEvent.ACTION_MOVE: - showMagnifier(event); + updateMagnifier(event); break; case MotionEvent.ACTION_UP: diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 6e87e2356f65db1efedcf51ee8a537afcccbbc96..3db149a442de647c1dae9e0866b9587be382a7de 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -24,6 +24,7 @@ import android.annotation.UiThread; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PixelFormat; @@ -74,6 +75,8 @@ public final class Magnifier { private final int mWindowWidth; // The height of the window containing the magnifier. private final int mWindowHeight; + // The zoom applied to the view region copied to the magnifier window. + private final float mZoom; // The width of the bitmaps where the magnifier content is copied. private final int mBitmapWidth; // The height of the bitmaps where the magnifier content is copied. @@ -110,10 +113,10 @@ public final class Magnifier { com.android.internal.R.dimen.magnifier_height); mWindowElevation = context.getResources().getDimension( com.android.internal.R.dimen.magnifier_elevation); - final float zoomScale = context.getResources().getFloat( + mZoom = context.getResources().getFloat( com.android.internal.R.dimen.magnifier_zoom_scale); - mBitmapWidth = Math.round(mWindowWidth / zoomScale); - mBitmapHeight = Math.round(mWindowHeight / zoomScale); + mBitmapWidth = Math.round(mWindowWidth / mZoom); + mBitmapHeight = Math.round(mWindowHeight / mZoom); // The view's surface coordinates will not be updated until the magnifier is first shown. mViewCoordinatesInSurface = new int[2]; } @@ -186,21 +189,6 @@ public final class Magnifier { } } - @Nullable - private Surface getValidViewSurface() { - // TODO: deduplicate this against the first part of #performPixelCopy - final Surface surface; - if (mView instanceof SurfaceView) { - surface = ((SurfaceView) mView).getHolder().getSurface(); - } else if (mView.getViewRootImpl() != null) { - surface = mView.getViewRootImpl().mSurface; - } else { - surface = null; - } - - return (surface != null && surface.isValid()) ? surface : null; - } - /** * Dismisses the magnifier from the screen. Calling this on a dismissed magnifier is a no-op. */ @@ -225,6 +213,44 @@ public final class Magnifier { } } + /** + * @return The width of the magnifier window, in pixels. + */ + public int getWidth() { + return mWindowWidth; + } + + /** + * @return The height of the magnifier window, in pixels. + */ + public int getHeight() { + return mWindowHeight; + } + + /** + * @return The zoom applied to the magnified view region copied to the magnifier window. + * If the zoom is x and the magnifier window size is (width, height), the original size + * of the content copied in the magnifier will be (width / x, height / x). + */ + public float getZoom() { + return mZoom; + } + + @Nullable + private Surface getValidViewSurface() { + // TODO: deduplicate this against the first part of #performPixelCopy + final Surface surface; + if (mView instanceof SurfaceView) { + surface = ((SurfaceView) mView).getHolder().getSurface(); + } else if (mView.getViewRootImpl() != null) { + surface = mView.getViewRootImpl().mSurface; + } else { + surface = null; + } + + return (surface != null && surface.isValid()) ? surface : null; + } + private void configureCoordinates(final float xPosInView, final float yPosInView) { // Compute the coordinates of the center of the content going to be displayed in the // magnifier. These are relative to the surface the content is copied from. @@ -319,6 +345,10 @@ public final class Magnifier { * producing a shakiness effect for the magnifier content. */ private static class InternalPopupWindow { + // The alpha set on the magnifier's content, which defines how + // prominent the white background is. + private static final int CONTENT_BITMAP_ALPHA = 242; + // Display associated to the view the magnifier is attached to. private final Display mDisplay; // The size of the content of the magnifier. @@ -511,10 +541,13 @@ public final class Magnifier { final DisplayListCanvas canvas = mBitmapRenderNode.start(mContentWidth, mContentHeight); try { + canvas.drawColor(Color.WHITE); + final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight); final Paint paint = new Paint(); paint.setFilterBitmap(true); + paint.setAlpha(CONTENT_BITMAP_ALPHA); canvas.drawBitmap(mBitmap, srcRect, dstRect, paint); } finally { mBitmapRenderNode.end(canvas); @@ -594,7 +627,7 @@ public final class Magnifier { return null; } synchronized (mWindow.mLock) { - return mWindow.mBitmap; + return Bitmap.createScaledBitmap(mWindow.mBitmap, mWindowWidth, mWindowHeight, true); } } diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java index 7d556bf450b4350be1934a90882e12d4b6f441b5..3ec8ab9240bd26eae2c2570fa66a2a919faae387 100644 --- a/core/java/android/widget/MediaControlView2.java +++ b/core/java/android/widget/MediaControlView2.java @@ -20,15 +20,18 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.media.SessionToken2; import android.media.session.MediaController; import android.media.update.ApiLoader; import android.media.update.MediaControlView2Provider; import android.media.update.ViewGroupHelper; import android.util.AttributeSet; +import android.view.View; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +// TODO: Use link annotation to refer VideoView2 once VideoView2 became unhidden. /** * A View that contains the controls for MediaPlayer2. * It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward", @@ -55,10 +58,7 @@ import java.lang.annotation.RetentionPolicy; *

    * It is also possible to add custom buttons with custom icons and actions inside MediaControlView2. * Those buttons will be shown when the overflow button is clicked. - * See {@link VideoView2#setCustomActions} for more details on how to add. - * - * TODO PUBLIC API - * @hide + * See VideoView2#setCustomActions for more details on how to add. */ public class MediaControlView2 extends ViewGroupHelper { /** @hide */ @@ -80,65 +80,60 @@ public class MediaControlView2 extends ViewGroupHelper{@link #BUTTON_SETTINGS} * * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. + * @hide */ public void setButtonVisibility(@Button int button, @Visibility int visibility) { mProvider.setButtonVisibility_impl(button, visibility); @@ -203,4 +214,15 @@ public class MediaControlView2 extends ViewGroupHelper= 0) { + weight = Math.min(Typeface.MAX_WEIGHT, weight); + final boolean italic = (style & Typeface.ITALIC) != 0; + setTypeface(Typeface.create(typeface, weight, italic)); + } else { + setTypeface(typeface, style); } - - setTypeface(tf, styleIndex); } private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) { @@ -2087,7 +2111,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle */ - public void setTypeface(Typeface tf, int style) { + public void setTypeface(@Nullable Typeface tf, @Typeface.Style int style) { if (style > 0) { if (tf == null) { tf = Typeface.defaultFromStyle(style); @@ -3392,6 +3416,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean mFontFamilyExplicit = false; int mTypefaceIndex = -1; int mStyleIndex = -1; + int mFontWeight = -1; boolean mAllCaps = false; int mShadowColor = 0; float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0; @@ -3416,6 +3441,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " mFontFamilyExplicit:" + mFontFamilyExplicit + "\n" + " mTypefaceIndex:" + mTypefaceIndex + "\n" + " mStyleIndex:" + mStyleIndex + "\n" + + " mFontWeight:" + mFontWeight + "\n" + " mAllCaps:" + mAllCaps + "\n" + " mShadowColor:" + mShadowColor + "\n" + " mShadowDx:" + mShadowDx + "\n" @@ -3451,6 +3477,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener com.android.internal.R.styleable.TextAppearance_fontFamily); sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle, com.android.internal.R.styleable.TextAppearance_textStyle); + sAppearanceValues.put(com.android.internal.R.styleable.TextView_textFontWeight, + com.android.internal.R.styleable.TextAppearance_textFontWeight); sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps, com.android.internal.R.styleable.TextAppearance_textAllCaps); sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor, @@ -3536,6 +3564,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_textStyle: attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex); break; + case com.android.internal.R.styleable.TextAppearance_textFontWeight: + attributes.mFontWeight = appearance.getInt(attr, attributes.mFontWeight); + break; case com.android.internal.R.styleable.TextAppearance_textAllCaps: attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps); break; @@ -3598,7 +3629,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener attributes.mFontFamily = null; } setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily, - attributes.mTypefaceIndex, attributes.mStyleIndex); + attributes.mTypefaceIndex, attributes.mStyleIndex, attributes.mFontWeight); if (attributes.mShadowColor != 0) { setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy, @@ -3865,7 +3896,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle */ - public void setTypeface(Typeface tf) { + public void setTypeface(@Nullable Typeface tf) { if (mTextPaint.getTypeface() != tf) { mTextPaint.setTypeface(tf); @@ -4110,6 +4141,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public void setTextMetricsParams(@NonNull PrecomputedText.Params params) { mTextPaint.set(params.getTextPaint()); + mUserSetTextScaleX = true; mTextDir = params.getTextDirection(); mBreakStrategy = params.getBreakStrategy(); mHyphenationFrequency = params.getHyphenationFrequency(); @@ -5497,9 +5529,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * {@link android.text.Editable.Factory} to create final or intermediate * {@link Editable Editables}. * + * If the passed text is a {@link PrecomputedText} but the parameters used to create the + * PrecomputedText mismatches with this TextView, IllegalArgumentException is thrown. To ensure + * the parameters match, you can call {@link TextView#setTextMetricsParams} before calling this. + * * @param text text to be displayed * * @attr ref android.R.styleable#TextView_text + * @throws IllegalArgumentException if the passed text is a {@link PrecomputedText} but the + * parameters used to create the PrecomputedText mismatches + * with this TextView. */ @android.view.RemotableViewMethod public final void setText(CharSequence text) { @@ -5613,7 +5652,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } else if (type == BufferType.SPANNABLE || mMovement != null) { text = mSpannableFactory.newSpannable(text); - } else if (!(text instanceof PrecomputedText || text instanceof CharWrapper)) { + } else if (text instanceof PrecomputedText) { + PrecomputedText precomputed = (PrecomputedText) text; + if (mTextDir == null) { + mTextDir = getTextDirectionHeuristic(); + } + if (!precomputed.getParams().isSameTextMetricsInternal( + getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency)) { + throw new IllegalArgumentException( + "PrecomputedText's Parameters don't match the parameters of this TextView." + + "Consider using setTextMetricsParams(precomputedText.getParams()) " + + "to override the settings of this TextView: " + + "PrecomputedText: " + precomputed.getParams() + + "TextView: " + getTextMetricsParams()); + } + } else if (!(text instanceof CharWrapper)) { text = TextUtils.stringOrSpannedString(text); } @@ -5938,15 +5991,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean forceUpdate = false; if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); - setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0); + setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, + Typeface.NORMAL, -1 /* weight, not specifeid */); } else if (isVisiblePassword) { if (mTransformation == PasswordTransformationMethod.getInstance()) { forceUpdate = true; } - setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0); + setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, + Typeface.NORMAL, -1 /* weight, not specified */); } else if (wasPassword || wasVisiblePassword) { // not in password mode, clean up typeface and transformation - setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, -1, -1); + setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, + DEFAULT_TYPEFACE /* typeface index */, Typeface.NORMAL, + -1 /* weight, not specified */); if (mTransformation == PasswordTransformationMethod.getInstance()) { forceUpdate = true; } @@ -8027,7 +8084,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - private void nullLayouts() { + /** @hide */ + @VisibleForTesting + public void nullLayouts() { if (mLayout instanceof BoringLayout && mSavedLayout == null) { mSavedLayout = (BoringLayout) mLayout; } @@ -8121,7 +8180,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * not the full view width with padding. * {@hide} */ - protected void makeNewLayout(int wantWidth, int hintWidth, + @VisibleForTesting + public void makeNewLayout(int wantWidth, int hintWidth, BoringLayout.Metrics boring, BoringLayout.Metrics hintBoring, int ellipsisWidth, boolean bringIntoView) { @@ -8411,7 +8471,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mIncludePad; } - private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics(); + /** @hide */ + @VisibleForTesting + public static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics(); @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -11464,12 +11526,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @return Whether or not we're attempting to start the action mode. * @hide */ - public boolean requestActionMode(@NonNull TextLinks.TextLink link) { + public boolean requestActionMode(@NonNull TextLinks.TextLinkSpan clickedSpan) { + Preconditions.checkNotNull(clickedSpan); + final TextLinks.TextLink link = clickedSpan.getTextLink(); Preconditions.checkNotNull(link); createEditorIfNeeded(); - mEditor.startLinkActionModeAsync(link); + + if (!(mText instanceof Spanned)) { + return false; + } + + final int start = ((Spanned) mText).getSpanStart(clickedSpan); + final int end = ((Spanned) mText).getSpanEnd(clickedSpan); + + if (start < 0 || end < 1) { + return false; + } + + mEditor.startLinkActionModeAsync(start, end); return true; } + /** * @hide */ diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index edcf209bdf818b91c831c56b0dd9dad7baa48e2d..d74a60e483e57c747bee290424eee20c4f0cad88 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -531,6 +531,14 @@ public class Toast { mWM.removeViewImmediate(mView); } + + // Now that we've removed the view it's safe for the server to release + // the resources. + try { + getService().finishToken(mPackageName, this); + } catch (RemoteException e) { + } + mView = null; } } diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java index a7ae3234c4d3dc7be9367129dd37d7d3a58d90af..6f08dc22aff1d1e131c1c7725373ffcc7c59c2b6 100644 --- a/core/java/android/widget/VideoView2.java +++ b/core/java/android/widget/VideoView2.java @@ -22,10 +22,12 @@ import android.annotation.Nullable; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioManager; +import android.media.DataSourceDesc; +import android.media.MediaItem2; import android.media.MediaMetadata2; -import android.media.MediaPlayerBase; +import android.media.MediaPlayer2; +import android.media.SessionToken2; import android.media.session.MediaController; -import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.media.update.ApiLoader; import android.media.update.VideoView2Provider; @@ -45,14 +47,14 @@ import java.util.concurrent.Executor; // TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted. /** - * Displays a video file. VideoView2 class is a View class which is wrapping MediaPlayer2 so that - * developers can easily implement a video rendering application. + * Displays a video file. VideoView2 class is a View class which is wrapping {@link MediaPlayer2} + * so that developers can easily implement a video rendering application. * *

    * Data sources that VideoView2 supports : * VideoView2 can play video files and audio-only files as * well. It can load from various sources such as resources or content providers. The supported - * media file formats are the same as MediaPlayer2. + * media file formats are the same as {@link MediaPlayer2}. * *

    * View type can be selected : @@ -101,8 +103,6 @@ import java.util.concurrent.Executor; * does not restore the current play state, play position, selected tracks. Applications should save * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and * {@link android.app.Activity#onRestoreInstanceState}. - * - * @hide */ public class VideoView2 extends ViewGroupHelper { /** @hide */ @@ -199,11 +199,23 @@ public class VideoView2 extends ViewGroupHelper { * before calling this method. * * @throws IllegalStateException if interal MediaSession is not created yet. + * @hide TODO: remove */ public MediaController getMediaController() { return mProvider.getMediaController_impl(); } + /** + * Returns {@link android.media.SessionToken2} so that developers create their own + * {@link android.media.MediaController2} instance. This method should be called when VideoView2 + * is attached to window, or it throws IllegalStateException. + * + * @throws IllegalStateException if interal MediaSession is not created yet. + */ + public SessionToken2 getMediaSessionToken() { + return mProvider.getMediaSessionToken_impl(); + } + /** * Shows or hides closed caption or subtitles if there is any. * The first subtitle track will be chosen if there multiple subtitle tracks exist. @@ -265,43 +277,12 @@ public class VideoView2 extends ViewGroupHelper { mProvider.setAudioAttributes_impl(attributes); } - /** - * Sets a remote player for handling playback of the selected route from MediaControlView2. - * If this is not called, MediaCotrolView2 will not show the route button. - * - * @param routeCategories the list of media control categories in - * {@link android.support.v7.media.MediaControlIntent} - * @param player the player to handle playback of the selected route. - * If null, a default route player will be used. - * @throws IllegalStateException if MediaControlView2 is not set. - * @hide - */ - public void setRouteAttributes(@NonNull List routeCategories, - @Nullable MediaPlayerBase player) { - mProvider.setRouteAttributes_impl(routeCategories, player); - } - - /** - * Sets a remote player for handling playback of the selected route from MediaControlView2. - * If this is not called, MediaCotrolView2 will not show the route button. - * - * @param routeCategories the list of media control categories in - * {@link android.support.v7.media.MediaControlIntent} - * @param sessionPlayer the player to handle playback of the selected route. - * If null, a default route player will be used. - * @throws IllegalStateException if MediaControlView2 is not set. - * @hide - */ - // TODO: Use MediaPlayerBase once MediaSession2 APIs are ready. - public void setRouteAttributes(@NonNull List routeCategories, - @Nullable MediaSession.Callback sessionPlayer) { - mProvider.setRouteAttributes_impl(routeCategories, sessionPlayer); - } - /** * Sets video path. * * @param path the path of the video. + * + * @hide TODO remove */ public void setVideoPath(String path) { mProvider.setVideoPath_impl(path); @@ -311,6 +292,8 @@ public class VideoView2 extends ViewGroupHelper { * Sets video URI. * * @param uri the URI of the video. + * + * @hide TODO remove */ public void setVideoUri(Uri uri) { mProvider.setVideoUri_impl(uri); @@ -325,11 +308,32 @@ public class VideoView2 extends ViewGroupHelper { * changed with key/value pairs through the headers parameter with * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value * to disallow or allow cross domain redirection. + * + * @hide TODO remove */ public void setVideoUri(Uri uri, Map headers) { mProvider.setVideoUri_impl(uri, headers); } + /** + * Sets {@link MediaItem2} object to render using VideoView2. Alternative way to set media + * object to VideoView2 is {@link #setDataSource}. + * @param mediaItem the MediaItem2 to play + * @see #setDataSource + */ + public void setMediaItem(@NonNull MediaItem2 mediaItem) { + mProvider.setMediaItem_impl(mediaItem); + } + + /** + * Sets {@link DataSourceDesc} object to render using VideoView2. + * @param dataSource the {@link DataSourceDesc} object to play. + * @see #setMediaItem + */ + public void setDataSource(@NonNull DataSourceDesc dataSource) { + mProvider.setDataSource_impl(dataSource); + } + /** * Selects which view will be used to render video between SurfacView and TextureView. * @@ -361,6 +365,7 @@ public class VideoView2 extends ViewGroupHelper { * in {@link MediaControlView2}. * @param executor executor to run callbacks on. * @param listener A listener to be called when a custom button is clicked. + * @hide TODO remove */ public void setCustomActions(List actionList, Executor executor, OnCustomActionListener listener) { @@ -371,7 +376,6 @@ public class VideoView2 extends ViewGroupHelper { * Registers a callback to be invoked when a view type change is done. * {@see #setViewType(int)} * @param l The callback that will be run - * * @hide */ @VisibleForTesting @@ -382,6 +386,7 @@ public class VideoView2 extends ViewGroupHelper { /** * Registers a callback to be invoked when the fullscreen mode should be changed. * @param l The callback that will be run + * @hide TODO remove */ public void setFullScreenRequestListener(OnFullScreenRequestListener l) { mProvider.setFullScreenRequestListener_impl(l); @@ -410,6 +415,7 @@ public class VideoView2 extends ViewGroupHelper { /** * Interface definition of a callback to be invoked to inform the fullscreen mode is changed. * Application should handle the fullscreen mode accordingly. + * @hide TODO remove */ public interface OnFullScreenRequestListener { /** @@ -420,8 +426,8 @@ public class VideoView2 extends ViewGroupHelper { /** * Interface definition of a callback to be invoked to inform that a custom action is performed. + * @hide TODO remove */ - // TODO: When MediaSession2 is ready, modify the method to match the signature. public interface OnCustomActionListener { /** * Called to indicate that a custom action is performed. diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index ffc21d5ea9e1f9c8e2e7c2f0ae0e0ad710a7232d..90712fdbde4fdb4bc1a03277c743c769e33869f6 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -143,6 +143,9 @@ public class AccessibilityShortcutController { mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN), false, co, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN), + false, co, UserHandle.USER_ALL); setCurrentUser(mUserId); } @@ -168,8 +171,12 @@ public class AccessibilityShortcutController { final ContentResolver cr = mContext.getContentResolver(); final boolean enabled = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, mUserId) == 1; + // Enable the shortcut from the lockscreen by default if the dialog has been shown + final int dialogAlreadyShown = Settings.Secure.getIntForUser( + cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, mUserId); mEnabledOnLockScreen = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, 0, mUserId) == 1; + cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, + dialogAlreadyShown, mUserId) == 1; mIsShortcutEnabled = enabled && haveValidService; } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index fabda4a5c15e7caa500a75d037d3b03c86357743..2505ea5d448b685afba5a43618fdb5e3dce373f1 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -26,7 +26,8 @@ interface IAppOpsService { // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h int checkOperation(int code, int uid, String packageName); int noteOperation(int code, int uid, String packageName); - int startOperation(IBinder token, int code, int uid, String packageName); + int startOperation(IBinder token, int code, int uid, String packageName, + boolean startIfModeDefault); void finishOperation(IBinder token, int code, int uid, String packageName); void startWatchingMode(int op, String packageName, IAppOpsCallback callback); void stopWatchingMode(IAppOpsCallback callback); diff --git a/core/java/com/android/internal/globalactions/Action.java b/core/java/com/android/internal/globalactions/Action.java new file mode 100644 index 0000000000000000000000000000000000000000..ddb75c1cb11bf8d98e45f2559a64dd6400c7416b --- /dev/null +++ b/core/java/com/android/internal/globalactions/Action.java @@ -0,0 +1,42 @@ +/* + * 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 com.android.internal.globalactions; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** What each item in the global actions dialog must be able to support. */ +public interface Action { + /** @return Text that will be announced when dialog is created. {@code null} for none. */ + CharSequence getLabelForAccessibility(Context context); + + /** Create the view that represents this action. */ + View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater); + + /** Called when the action is selected by the user. */ + void onPress(); + + /** @return whether this action should appear in the dialog when the keygaurd is showing. */ + boolean showDuringKeyguard(); + + /** @return whether this action should appear in the dialog before the device is provisioned. */ + boolean showBeforeProvisioning(); + + /** @return {@code true} if the action is enabled for user interaction. */ + boolean isEnabled(); +} diff --git a/core/java/com/android/internal/globalactions/ActionsAdapter.java b/core/java/com/android/internal/globalactions/ActionsAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c9f01ce9c08b06178da6b13e5579782cf126b109 --- /dev/null +++ b/core/java/com/android/internal/globalactions/ActionsAdapter.java @@ -0,0 +1,112 @@ +/* + * 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 com.android.internal.globalactions; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import java.util.function.BooleanSupplier; +import java.util.List; + +/** + * The adapter used for the list within the global actions dialog, taking into account whether the + * keyguard is showing via {@link LegacyGlobalActions#mKeyguardShowing} and whether the device is + * provisioned via {@link LegacyGlobalActions#mDeviceProvisioned}. + */ +public class ActionsAdapter extends BaseAdapter { + private final Context mContext; + private final List mItems; + private final BooleanSupplier mDeviceProvisioned; + private final BooleanSupplier mKeyguardShowing; + + public ActionsAdapter(Context context, List items, + BooleanSupplier deviceProvisioned, BooleanSupplier keyguardShowing) { + mContext = context; + mItems = items; + mDeviceProvisioned = deviceProvisioned; + mKeyguardShowing = keyguardShowing; + } + + @Override + public int getCount() { + final boolean keyguardShowing = mKeyguardShowing.getAsBoolean(); + final boolean deviceProvisioned = mDeviceProvisioned.getAsBoolean(); + int count = 0; + + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + + if (keyguardShowing && !action.showDuringKeyguard()) { + continue; + } + if (!deviceProvisioned && !action.showBeforeProvisioning()) { + continue; + } + count++; + } + return count; + } + + @Override + public boolean isEnabled(int position) { + return getItem(position).isEnabled(); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public Action getItem(int position) { + final boolean keyguardShowing = mKeyguardShowing.getAsBoolean(); + final boolean deviceProvisioned = mDeviceProvisioned.getAsBoolean(); + + int filteredPos = 0; + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + if (keyguardShowing && !action.showDuringKeyguard()) { + continue; + } + if (!deviceProvisioned && !action.showBeforeProvisioning()) { + continue; + } + if (filteredPos == position) { + return action; + } + filteredPos++; + } + + throw new IllegalArgumentException("position " + position + + " out of range of showable actions" + + ", filtered count=" + getCount() + + ", keyguardshowing=" + keyguardShowing + + ", provisioned=" + deviceProvisioned); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Action action = getItem(position); + return action.create(mContext, convertView, parent, LayoutInflater.from(mContext)); + } +} diff --git a/core/java/com/android/internal/globalactions/ActionsDialog.java b/core/java/com/android/internal/globalactions/ActionsDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..1cca1cc64ca1519cf409648963fd52a42cc81fa9 --- /dev/null +++ b/core/java/com/android/internal/globalactions/ActionsDialog.java @@ -0,0 +1,94 @@ +/* + * 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 com.android.internal.globalactions; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.accessibility.AccessibilityEvent; +import android.view.KeyEvent; +import android.widget.ListView; +import com.android.internal.app.AlertController; + +/** A dialog that lists the given Action items to be user selectable. */ +public final class ActionsDialog extends Dialog implements DialogInterface { + private final Context mContext; + private final AlertController mAlert; + private final ActionsAdapter mAdapter; + + public ActionsDialog(Context context, AlertController.AlertParams params) { + super(context, getDialogTheme(context)); + mContext = getContext(); + mAlert = AlertController.create(mContext, this, getWindow()); + mAdapter = (ActionsAdapter) params.mAdapter; + params.apply(mAlert); + } + + private static int getDialogTheme(Context context) { + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme, + outValue, true); + return outValue.resourceId; + } + + @Override + protected void onStart() { + super.setCanceledOnTouchOutside(true); + super.onStart(); + } + + public ListView getListView() { + return mAlert.getListView(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAlert.installContent(); + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + for (int i = 0; i < mAdapter.getCount(); ++i) { + CharSequence label = + mAdapter.getItem(i).getLabelForAccessibility(getContext()); + if (label != null) { + event.getText().add(label); + } + } + } + return super.dispatchPopulateAccessibilityEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (mAlert.onKeyDown(keyCode, event)) { + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (mAlert.onKeyUp(keyCode, event)) { + return true; + } + return super.onKeyUp(keyCode, event); + } +} diff --git a/core/java/com/android/internal/globalactions/LongPressAction.java b/core/java/com/android/internal/globalactions/LongPressAction.java new file mode 100644 index 0000000000000000000000000000000000000000..eed4cd9fe9b32dc25b38853ee9f3b64a2d041a08 --- /dev/null +++ b/core/java/com/android/internal/globalactions/LongPressAction.java @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.globalactions; + +/** An action that also supports long press. */ +public interface LongPressAction extends Action { + boolean onLongPress(); +} diff --git a/core/java/com/android/internal/globalactions/SinglePressAction.java b/core/java/com/android/internal/globalactions/SinglePressAction.java new file mode 100644 index 0000000000000000000000000000000000000000..0b8cd0b2c266c45655c73ef202de20fdffda97fd --- /dev/null +++ b/core/java/com/android/internal/globalactions/SinglePressAction.java @@ -0,0 +1,99 @@ +/* + * 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 com.android.internal.globalactions; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.internal.R; + +/** A single press action maintains no state, just responds to a press and takes an action. */ +public abstract class SinglePressAction implements Action { + private final int mIconResId; + private final Drawable mIcon; + private final int mMessageResId; + private final CharSequence mMessage; + + protected SinglePressAction(int iconResId, int messageResId) { + mIconResId = iconResId; + mMessageResId = messageResId; + mMessage = null; + mIcon = null; + } + + protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) { + mIconResId = iconResId; + mMessageResId = 0; + mMessage = message; + mIcon = icon; + } + + @Override + public boolean isEnabled() { + return true; + } + + public String getStatus() { + return null; + } + + @Override + abstract public void onPress(); + + @Override + public CharSequence getLabelForAccessibility(Context context) { + if (mMessage != null) { + return mMessage; + } else { + return context.getString(mMessageResId); + } + } + + @Override + public View create( + Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { + View v = inflater.inflate(R.layout.global_actions_item, parent, false); + + ImageView icon = v.findViewById(R.id.icon); + TextView messageView = v.findViewById(R.id.message); + + TextView statusView = v.findViewById(R.id.status); + final String status = getStatus(); + if (!TextUtils.isEmpty(status)) { + statusView.setText(status); + } else { + statusView.setVisibility(View.GONE); + } + if (mIcon != null) { + icon.setImageDrawable(mIcon); + icon.setScaleType(ImageView.ScaleType.CENTER_CROP); + } else if (mIconResId != 0) { + icon.setImageDrawable(context.getDrawable(mIconResId)); + } + if (mMessage != null) { + messageView.setText(mMessage); + } else { + messageView.setText(mMessageResId); + } + + return v; + } +} diff --git a/core/java/com/android/internal/globalactions/ToggleAction.java b/core/java/com/android/internal/globalactions/ToggleAction.java new file mode 100644 index 0000000000000000000000000000000000000000..9167958612ea30f9d0286034951b8c122810c2d8 --- /dev/null +++ b/core/java/com/android/internal/globalactions/ToggleAction.java @@ -0,0 +1,155 @@ +/* + * 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 com.android.internal.globalactions; + +import android.content.Context; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.internal.R; + +/** + * A toggle action knows whether it is on or off, and displays an icon and status message + * accordingly. + */ +public abstract class ToggleAction implements Action { + private static final String TAG = "ToggleAction"; + + public enum State { + Off(false), + TurningOn(true), + TurningOff(true), + On(false); + + private final boolean inTransition; + + State(boolean intermediate) { + inTransition = intermediate; + } + + public boolean inTransition() { + return inTransition; + } + } + + protected State mState = State.Off; + + // prefs + protected int mEnabledIconResId; + protected int mDisabledIconResid; + protected int mMessageResId; + protected int mEnabledStatusMessageResId; + protected int mDisabledStatusMessageResId; + + /** + * @param enabledIconResId The icon for when this action is on. + * @param disabledIconResid The icon for when this action is off. + * @param message The general information message, e.g 'Silent Mode' + * @param enabledStatusMessageResId The on status message, e.g 'sound disabled' + * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled' + */ + public ToggleAction(int enabledIconResId, + int disabledIconResid, + int message, + int enabledStatusMessageResId, + int disabledStatusMessageResId) { + mEnabledIconResId = enabledIconResId; + mDisabledIconResid = disabledIconResid; + mMessageResId = message; + mEnabledStatusMessageResId = enabledStatusMessageResId; + mDisabledStatusMessageResId = disabledStatusMessageResId; + } + + /** Override to make changes to resource IDs just before creating the View. */ + void willCreate() { + + } + + @Override + public CharSequence getLabelForAccessibility(Context context) { + return context.getString(mMessageResId); + } + + @Override + public View create(Context context, View convertView, ViewGroup parent, + LayoutInflater inflater) { + willCreate(); + + View v = inflater.inflate(R.layout.global_actions_item, parent, false); + + ImageView icon = v.findViewById(R.id.icon); + TextView messageView = v.findViewById(R.id.message); + TextView statusView = v.findViewById(R.id.status); + final boolean enabled = isEnabled(); + + if (messageView != null) { + messageView.setText(mMessageResId); + messageView.setEnabled(enabled); + } + + boolean on = ((mState == State.On) || (mState == State.TurningOn)); + if (icon != null) { + icon.setImageDrawable(context.getDrawable( + (on ? mEnabledIconResId : mDisabledIconResid))); + icon.setEnabled(enabled); + } + + if (statusView != null) { + statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); + statusView.setVisibility(View.VISIBLE); + statusView.setEnabled(enabled); + } + v.setEnabled(enabled); + + return v; + } + + @Override + public final void onPress() { + if (mState.inTransition()) { + Log.w(TAG, "shouldn't be able to toggle when in transition"); + return; + } + + final boolean nowOn = !(mState == State.On); + onToggle(nowOn); + changeStateFromPress(nowOn); + } + + @Override + public boolean isEnabled() { + return !mState.inTransition(); + } + + /** + * Implementations may override this if their state can be in on of the intermediate + * states until some notification is received (e.g airplane mode is 'turning off' until + * we know the wireless connections are back online + * @param buttonOn Whether the button was turned on or off + */ + protected void changeStateFromPress(boolean buttonOn) { + mState = buttonOn ? State.On : State.Off; + } + + public abstract void onToggle(boolean on); + + public void updateState(State state) { + mState = state; + } +} diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index 5457c1d4cc0332164dc2835ab30894324ecc0630..5abc6d4f76b1fb2b430b98191a7d1b9d43075e3e 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -73,14 +73,16 @@ public class BatterySipper implements Comparable { public double usagePowerMah; // Subsystem usage times. + public long audioTimeMs; + public long bluetoothRunningTimeMs; + public long cameraTimeMs; + public long cpuFgTimeMs; public long cpuTimeMs; + public long flashlightTimeMs; public long gpsTimeMs; - public long wifiRunningTimeMs; - public long cpuFgTimeMs; + public long videoTimeMs; public long wakeLockTimeMs; - public long cameraTimeMs; - public long flashlightTimeMs; - public long bluetoothRunningTimeMs; + public long wifiRunningTimeMs; public long mobileRxPackets; public long mobileTxPackets; @@ -102,30 +104,33 @@ public class BatterySipper implements Comparable { // Measured in mAh (milli-ampere per hour). // These are included when summed. - public double wifiPowerMah; + public double audioPowerMah; + public double bluetoothPowerMah; + public double cameraPowerMah; public double cpuPowerMah; - public double wakeLockPowerMah; - public double mobileRadioPowerMah; + public double flashlightPowerMah; public double gpsPowerMah; + public double mobileRadioPowerMah; public double sensorPowerMah; - public double cameraPowerMah; - public double flashlightPowerMah; - public double bluetoothPowerMah; + public double videoPowerMah; + public double wakeLockPowerMah; + public double wifiPowerMah; public enum DrainType { - IDLE, - CELL, - PHONE, - WIFI, + AMBIENT_DISPLAY, + APP, BLUETOOTH, + CAMERA, + CELL, FLASHLIGHT, + IDLE, + MEMORY, + OVERCOUNTED, + PHONE, SCREEN, - APP, - USER, UNACCOUNTED, - OVERCOUNTED, - CAMERA, - MEMORY + USER, + WIFI, } public BatterySipper(DrainType drainType, Uid uid, double value) { @@ -176,10 +181,12 @@ public class BatterySipper implements Comparable { totalPowerMah += other.totalPowerMah; usageTimeMs += other.usageTimeMs; usagePowerMah += other.usagePowerMah; + audioTimeMs += other.audioTimeMs; cpuTimeMs += other.cpuTimeMs; gpsTimeMs += other.gpsTimeMs; wifiRunningTimeMs += other.wifiRunningTimeMs; cpuFgTimeMs += other.cpuFgTimeMs; + videoTimeMs += other.videoTimeMs; wakeLockTimeMs += other.wakeLockTimeMs; cameraTimeMs += other.cameraTimeMs; flashlightTimeMs += other.flashlightTimeMs; @@ -196,6 +203,7 @@ public class BatterySipper implements Comparable { wifiTxBytes += other.wifiTxBytes; btRxBytes += other.btRxBytes; btTxBytes += other.btTxBytes; + audioPowerMah += other.audioPowerMah; wifiPowerMah += other.wifiPowerMah; gpsPowerMah += other.gpsPowerMah; cpuPowerMah += other.cpuPowerMah; @@ -206,6 +214,7 @@ public class BatterySipper implements Comparable { flashlightPowerMah += other.flashlightPowerMah; bluetoothPowerMah += other.bluetoothPowerMah; screenPowerMah += other.screenPowerMah; + videoPowerMah += other.videoPowerMah; proportionalSmearMah += other.proportionalSmearMah; totalSmearedPowerMah += other.totalSmearedPowerMah; } @@ -219,7 +228,7 @@ public class BatterySipper implements Comparable { public double sumPower() { totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah + - flashlightPowerMah + bluetoothPowerMah; + flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah; totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah; return totalPowerMah; diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 5a59e70865e5f17ed041a9b6603675892cbd37a5..1e5bd1894843df44f7cff2f5b9d6b7a7d7c10edb 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -136,6 +136,7 @@ public class BatteryStatsHelper { PowerCalculator mCameraPowerCalculator; PowerCalculator mFlashlightPowerCalculator; PowerCalculator mMemoryPowerCalculator; + PowerCalculator mMediaPowerCalculator; boolean mHasWifiPowerReporting = false; boolean mHasBluetoothPowerReporting = false; @@ -424,6 +425,11 @@ public class BatteryStatsHelper { } mFlashlightPowerCalculator.reset(); + if (mMediaPowerCalculator == null) { + mMediaPowerCalculator = new MediaPowerCalculator(mPowerProfile); + } + mMediaPowerCalculator.reset(); + mStatsType = statsType; mRawUptimeUs = rawUptimeUs; mRawRealtimeUs = rawRealtimeUs; @@ -560,6 +566,7 @@ public class BatteryStatsHelper { mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); + mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); final double totalPower = app.sumPower(); if (DEBUG && totalPower != 0) { @@ -643,6 +650,21 @@ public class BatteryStatsHelper { } } + /** + * Ambient display power is the additional power the screen takes while in ambient display/ + * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should + * be hidden {@link #shouldHideSipper(BatterySipper)}, but should not be included in smearing + * {@link #removeHiddenBatterySippers(List)}. + */ + private void addAmbientDisplayUsage() { + long ambientDisplayMs = mStats.getScreenDozeTime(mRawRealtimeUs, mStatsType); + double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY) + * ambientDisplayMs / (60 * 60 * 1000); + if (power > 0) { + addEntry(DrainType.AMBIENT_DISPLAY, ambientDisplayMs, power); + } + } + private void addRadioUsage() { BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs, @@ -741,6 +763,7 @@ public class BatteryStatsHelper { addUserUsage(); addPhoneUsage(); addScreenUsage(); + addAmbientDisplayUsage(); addWiFiUsage(); addBluetoothUsage(); addMemoryUsage(); @@ -841,12 +864,13 @@ public class BatteryStatsHelper { final BatterySipper sipper = sippers.get(i); sipper.shouldHide = shouldHideSipper(sipper); if (sipper.shouldHide) { - if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED - && sipper.drainType != BatterySipper.DrainType.SCREEN - && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED - && sipper.drainType != BatterySipper.DrainType.BLUETOOTH - && sipper.drainType != BatterySipper.DrainType.WIFI - && sipper.drainType != BatterySipper.DrainType.IDLE) { + if (sipper.drainType != DrainType.OVERCOUNTED + && sipper.drainType != DrainType.SCREEN + && sipper.drainType != DrainType.AMBIENT_DISPLAY + && sipper.drainType != DrainType.UNACCOUNTED + && sipper.drainType != DrainType.BLUETOOTH + && sipper.drainType != DrainType.WIFI + && sipper.drainType != DrainType.IDLE) { // Don't add it if it is overcounted, unaccounted or screen proportionalSmearPowerMah += sipper.totalPowerMah; } @@ -893,13 +917,14 @@ public class BatteryStatsHelper { * Check whether we should hide the battery sipper. */ public boolean shouldHideSipper(BatterySipper sipper) { - final BatterySipper.DrainType drainType = sipper.drainType; - - return drainType == BatterySipper.DrainType.IDLE - || drainType == BatterySipper.DrainType.CELL - || drainType == BatterySipper.DrainType.SCREEN - || drainType == BatterySipper.DrainType.UNACCOUNTED - || drainType == BatterySipper.DrainType.OVERCOUNTED + final DrainType drainType = sipper.drainType; + + return drainType == DrainType.IDLE + || drainType == DrainType.CELL + || drainType == DrainType.SCREEN + || drainType == DrainType.AMBIENT_DISPLAY + || drainType == DrainType.UNACCOUNTED + || drainType == DrainType.OVERCOUNTED || isTypeService(sipper) || isTypeSystem(sipper); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 5966a8665f41c1258e60b5697410f62083e8a364..4ab2fecbe9645af08f5ff44cf97f4be8aa1e2157 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -7872,12 +7872,10 @@ public class BatteryStatsImpl extends BatteryStats { return; } final int deferredCount = mJobsDeferredCount.getCountLocked(which); - final long averageLatency = deferredEventCount != 0 - ? mJobsFreshnessTimeMs.getCountLocked(which) / deferredEventCount - : 0L; + final long totalLatency = mJobsFreshnessTimeMs.getCountLocked(which); sb.append(deferredEventCount); sb.append(','); sb.append(deferredCount); sb.append(','); - sb.append(averageLatency); + sb.append(totalLatency); for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) { if (mJobsFreshnessBuckets[i] == null) { sb.append(",0"); @@ -7896,12 +7894,10 @@ public class BatteryStatsImpl extends BatteryStats { return; } final int deferredCount = mJobsDeferredCount.getCountLocked(which); - final long averageLatency = deferredEventCount != 0 - ? mJobsFreshnessTimeMs.getCountLocked(which) / deferredEventCount - : 0L; + final long totalLatency = mJobsFreshnessTimeMs.getCountLocked(which); sb.append("times="); sb.append(deferredEventCount); sb.append(", "); sb.append("count="); sb.append(deferredCount); sb.append(", "); - sb.append("avgLatency="); sb.append(averageLatency); sb.append(", "); + sb.append("totalLatencyMs="); sb.append(totalLatency); sb.append(", "); for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) { sb.append("<"); sb.append(JOB_FRESHNESS_BUCKETS[i]); sb.append("ms="); if (mJobsFreshnessBuckets[i] == null) { @@ -11597,7 +11593,7 @@ public class BatteryStatsImpl extends BatteryStats { * time at the highest power level. * @param activityInfo */ - private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) { + private synchronized void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) { if (activityInfo == null) { return; } @@ -12606,7 +12602,7 @@ public class BatteryStatsImpl extends BatteryStats { temp = Math.max(0, temp); reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null, - status, plugType, level, temp); + status, plugType, level); final boolean onBattery = isOnBattery(plugType, status); final long uptime = mClocks.uptimeMillis(); @@ -12805,7 +12801,7 @@ public class BatteryStatsImpl extends BatteryStats { // Inform StatsLog of setBatteryState changes. // If this is the first reporting, pass in recentPast == null. private void reportChangesToStatsLog(HistoryItem recentPast, - final int status, final int plugType, final int level, final int temp) { + final int status, final int plugType, final int level) { if (recentPast == null || recentPast.batteryStatus != status) { StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, status); @@ -12816,8 +12812,6 @@ public class BatteryStatsImpl extends BatteryStats { if (recentPast == null || recentPast.batteryLevel != level) { StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, level); } - // Let's just always print the temperature, regardless of whether it changed. - StatsLog.write(StatsLog.DEVICE_TEMPERATURE_REPORTED, temp); } public long getAwakeTimeBattery() { @@ -13350,7 +13344,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; - private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false; + private static final boolean DEFAULT_READ_BINARY_CPU_TIME = true; private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000; private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000; @@ -13438,6 +13432,7 @@ public class BatteryStatsImpl extends BatteryStats { private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) { KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs; if (oldTimeMs != newTimeMs) { + mKernelUidCpuTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuFreqTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuActiveTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME); mKernelUidCpuClusterTimeReader diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java index 4d569053979ba9696d1ed0cdb142943d650a65fc..396deb4d7a0a0f5964ea685ada551c09aa8f2819 100644 --- a/core/java/com/android/internal/os/KernelCpuProcReader.java +++ b/core/java/com/android/internal/os/KernelCpuProcReader.java @@ -22,10 +22,12 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; @@ -124,6 +126,11 @@ public class KernelCpuProcReader { } fc.position(0); } + } catch (NoSuchFileException | FileNotFoundException e) { + // Happens when the kernel does not provide this file. Not a big issue. Just log it. + mErrors++; + Slog.w(TAG, "File not exist: " + mProc); + return null; } catch (IOException e) { mErrors++; Slog.e(TAG, "Error reading: " + mProc, e); diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java index 2519412f32467cbfe7bc33501a5814375e0b2d93..e790e08520cbc869a1cc087643348907e6115581 100644 --- a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java @@ -17,7 +17,6 @@ package com.android.internal.os; import android.annotation.Nullable; -import android.os.SystemClock; import android.util.Slog; import android.util.SparseArray; @@ -46,20 +45,17 @@ import java.nio.IntBuffer; * which has a shorter throttle interval and returns cached result from last read when the request * is throttled. * - * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to - * the nature of {@link #readDelta(Callback)}). + * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each + * caller has its own view of delta. */ -public class KernelUidCpuActiveTimeReader { - private static final String TAG = "KernelUidCpuActiveTimeReader"; - // Throttle interval in milliseconds - private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L; +public class KernelUidCpuActiveTimeReader extends + KernelUidCpuTimeReaderBase { + private static final String TAG = KernelUidCpuActiveTimeReader.class.getSimpleName(); private final KernelCpuProcReader mProcReader; - private long mLastTimeReadMs = Long.MIN_VALUE; - private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private SparseArray mLastUidCpuActiveTimeMs = new SparseArray<>(); - public interface Callback { + public interface Callback extends KernelUidCpuTimeReaderBase.Callback { /** * Notifies when new data is available. * @@ -78,11 +74,8 @@ public class KernelUidCpuActiveTimeReader { mProcReader = procReader; } - public void readDelta(@Nullable Callback cb) { - if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) { - Slog.w(TAG, "Throttle"); - return; - } + @Override + protected void readDeltaImpl(@Nullable Callback cb) { synchronized (mProcReader) { final ByteBuffer bytes = mProcReader.readBytes(); if (bytes == null || bytes.remaining() <= 4) { @@ -124,14 +117,20 @@ public class KernelUidCpuActiveTimeReader { } } } - // Slog.i(TAG, "Read uids: " + numUids); + if (DEBUG) { + Slog.d(TAG, "Read uids: " + numUids); + } } - mLastTimeReadMs = SystemClock.elapsedRealtime(); } - public void setThrottleInterval(long throttleInterval) { - if (throttleInterval >= 0) { - mThrottleInterval = throttleInterval; + public void readAbsolute(Callback cb) { + synchronized (mProcReader) { + readDelta(null); + int total = mLastUidCpuActiveTimeMs.size(); + for (int i = 0; i < total; i ++){ + int uid = mLastUidCpuActiveTimeMs.keyAt(i); + cb.onUidCpuActiveTime(uid, mLastUidCpuActiveTimeMs.get(uid).longValue()); + } } } diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java index 41ef8f05f0d1c6a033e1dfae2ab46a816b1b5af3..bf5b5203eb3f733ae88fe2e1c062b12c7b08ad83 100644 --- a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java @@ -17,7 +17,6 @@ package com.android.internal.os; import android.annotation.Nullable; -import android.os.SystemClock; import android.util.Slog; import android.util.SparseArray; @@ -50,17 +49,14 @@ import java.nio.IntBuffer; * which has a shorter throttle interval and returns cached result from last read when the request * is throttled. * - * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to - * the nature of {@link #readDelta(Callback)}). + * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each + * caller has its own view of delta. */ -public class KernelUidCpuClusterTimeReader { - private static final String TAG = "KernelUidCpuClusterTimeReader"; - // Throttle interval in milliseconds - private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L; +public class KernelUidCpuClusterTimeReader extends + KernelUidCpuTimeReaderBase { + private static final String TAG = KernelUidCpuClusterTimeReader.class.getSimpleName(); private final KernelCpuProcReader mProcReader; - private long mLastTimeReadMs = Long.MIN_VALUE; - private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private SparseArray mLastUidPolicyTimeMs = new SparseArray<>(); private int mNumClusters = -1; @@ -69,8 +65,9 @@ public class KernelUidCpuClusterTimeReader { private double[] mCurTime; // Reuse to avoid GC. private long[] mDeltaTime; // Reuse to avoid GC. + private long[] mCurTimeRounded; // Reuse to avoid GC. - public interface Callback { + public interface Callback extends KernelUidCpuTimeReaderBase.Callback { /** * Notifies when new data is available. * @@ -90,17 +87,8 @@ public class KernelUidCpuClusterTimeReader { mProcReader = procReader; } - public void setThrottleInterval(long throttleInterval) { - if (throttleInterval >= 0) { - mThrottleInterval = throttleInterval; - } - } - - public void readDelta(@Nullable Callback cb) { - if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) { - Slog.w(TAG, "Throttle"); - return; - } + @Override + protected void readDeltaImpl(@Nullable Callback cb) { synchronized (mProcReader) { ByteBuffer bytes = mProcReader.readBytes(); if (bytes == null || bytes.remaining() <= 4) { @@ -142,14 +130,30 @@ public class KernelUidCpuClusterTimeReader { int numUids = buf.remaining() / (mNumCores + 1); for (int i = 0; i < numUids; i++) { - processUidLocked(buf, cb); + processUid(buf, cb); + } + if (DEBUG) { + Slog.d(TAG, "Read uids: " + numUids); + } + } + } + + public void readAbsolute(Callback cb) { + synchronized (mProcReader) { + readDelta(null); + int total = mLastUidPolicyTimeMs.size(); + for (int i = 0; i < total; i ++){ + int uid = mLastUidPolicyTimeMs.keyAt(i); + double[] lastTimes = mLastUidPolicyTimeMs.get(uid); + for (int j = 0; j < mNumClusters; j++) { + mCurTimeRounded[j] = (long) lastTimes[j]; + } + cb.onUidCpuPolicyTime(uid, mCurTimeRounded); } - // Slog.i(TAG, "Read uids: " + numUids); } - mLastTimeReadMs = SystemClock.elapsedRealtime(); } - private void processUidLocked(IntBuffer buf, @Nullable Callback cb) { + private void processUid(IntBuffer buf, @Nullable Callback cb) { int uid = buf.get(); double[] lastTimes = mLastUidPolicyTimeMs.get(uid); if (lastTimes == null) { @@ -201,6 +205,7 @@ public class KernelUidCpuClusterTimeReader { mNumCoresOnCluster = numCoresOnCluster; mCurTime = new double[numClusters]; mDeltaTime = new long[numClusters]; + mCurTimeRounded = new long[numClusters]; return true; } diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java index a21a70e1d2c9a5c1bb2157c28adeaf20dbf0cb55..f65074f65d874349a01c61f3eb407ee62afaa866 100644 --- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java @@ -59,24 +59,21 @@ import java.nio.IntBuffer; * which has a shorter throttle interval and returns cached result from last read when the request * is throttled. * - * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to - * the nature of {@link #readDelta(Callback)}). + * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each + * caller has its own view of delta. */ -public class KernelUidCpuFreqTimeReader { - private static final boolean DEBUG = false; - private static final String TAG = "KernelUidCpuFreqTimeReader"; +public class KernelUidCpuFreqTimeReader extends + KernelUidCpuTimeReaderBase { + private static final String TAG = KernelUidCpuFreqTimeReader.class.getSimpleName(); static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state"; - // Throttle interval in milliseconds - private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L; - public interface Callback { + public interface Callback extends KernelUidCpuTimeReaderBase.Callback { void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs); } private long[] mCpuFreqs; private long[] mCurTimes; // Reuse to prevent GC. private long[] mDeltaTimes; // Reuse to prevent GC. - private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; private int mCpuFreqsCount; private long mLastTimeReadMs = Long.MIN_VALUE; private long mNowTimeMs; @@ -150,30 +147,20 @@ public class KernelUidCpuFreqTimeReader { mReadBinary = readBinary; } - public void setThrottleInterval(long throttleInterval) { - if (throttleInterval >= 0) { - mThrottleInterval = throttleInterval; - } - } - - public void readDelta(@Nullable Callback callback) { + @Override + protected void readDeltaImpl(@Nullable Callback callback) { if (mCpuFreqs == null) { return; } - if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) { - Slog.w(TAG, "Throttle"); - return; - } - mNowTimeMs = SystemClock.elapsedRealtime(); if (mReadBinary) { readDeltaBinary(callback); } else { readDeltaString(callback); } - mLastTimeReadMs = mNowTimeMs; } private void readDeltaString(@Nullable Callback callback) { + mNowTimeMs = SystemClock.elapsedRealtime(); final int oldMask = StrictMode.allowThreadDiskReadsMask(); try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { readDelta(reader, callback); @@ -182,6 +169,7 @@ public class KernelUidCpuFreqTimeReader { } finally { StrictMode.setThreadPolicyMask(oldMask); } + mLastTimeReadMs = mNowTimeMs; } @VisibleForTesting @@ -232,7 +220,20 @@ public class KernelUidCpuFreqTimeReader { } } } - // Slog.i(TAG, "Read uids: "+numUids); + if (DEBUG) { + Slog.d(TAG, "Read uids: " + numUids); + } + } + } + + public void readAbsolute(Callback cb) { + synchronized (mProcReader) { + readDelta(null); + int total = mLastUidCpuFreqTimeMs.size(); + for (int i = 0; i < total; i ++){ + int uid = mLastUidCpuFreqTimeMs.keyAt(i); + cb.onUidCpuFreqTime(uid, mLastUidCpuFreqTimeMs.get(uid)); + } } } diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java index 444049e7e415c52c641441d6d01b4641255a4c1b..97b7211e5e878711a1559f161c2e9e3068ade49d 100644 --- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -38,18 +38,19 @@ import java.io.IOException; * maintains the previous results of a call to {@link #readDelta} in order to provide a proper * delta. */ -public class KernelUidCpuTimeReader { - private static final String TAG = "KernelUidCpuTimeReader"; +public class KernelUidCpuTimeReader extends + KernelUidCpuTimeReaderBase { + private static final String TAG = KernelUidCpuTimeReader.class.getSimpleName(); private static final String sProcFile = "/proc/uid_cputime/show_uid_stat"; private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range"; /** * Callback interface for processing each line of the proc file. */ - public interface Callback { + public interface Callback extends KernelUidCpuTimeReaderBase.Callback { /** - * @param uid UID of the app - * @param userTimeUs time spent executing in user space in microseconds + * @param uid UID of the app + * @param userTimeUs time spent executing in user space in microseconds * @param systemTimeUs time spent executing in kernel space in microseconds */ void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs); @@ -61,11 +62,13 @@ public class KernelUidCpuTimeReader { /** * Reads the proc file, calling into the callback with a delta of time for each UID. + * * @param callback The callback to invoke for each line of the proc file. If null, * the data is consumed and subsequent calls to readDelta will provide * a fresh delta. */ - public void readDelta(@Nullable Callback callback) { + @Override + protected void readDeltaImpl(@Nullable Callback callback) { final int oldMask = StrictMode.allowThreadDiskReadsMask(); long nowUs = SystemClock.elapsedRealtime() * 1000; try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { @@ -132,7 +135,34 @@ public class KernelUidCpuTimeReader { } /** - * Removes the UID from the kernel module and from internal accounting data. + * Reads the proc file, calling into the callback with raw absolute value of time for each UID. + * @param callback The callback to invoke for each line of the proc file. + */ + public void readAbsolute(Callback callback) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { + TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); + String line; + while ((line = reader.readLine()) != null) { + splitter.setString(line); + final String uidStr = splitter.next(); + final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10); + final long userTimeUs = Long.parseLong(splitter.next(), 10); + final long systemTimeUs = Long.parseLong(splitter.next(), 10); + callback.onUidCpuTime(uid, userTimeUs, systemTimeUs); + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage()); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } + + /** + * Removes the UID from the kernel module and from internal accounting data. Only + * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is + * visible system wide. + * * @param uid The UID to remove. */ public void removeUid(int uid) { @@ -145,9 +175,12 @@ public class KernelUidCpuTimeReader { } /** - * Removes UIDs in a given range from the kernel module and internal accounting data. + * Removes UIDs in a given range from the kernel module and internal accounting data. Only + * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is + * visible system wide. + * * @param startUid the first uid to remove - * @param endUid the last uid to remove + * @param endUid the last uid to remove */ public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java b/core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java new file mode 100644 index 0000000000000000000000000000000000000000..11e50e1ecb95e6159b91ade7512e74ab09220501 --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package com.android.internal.os; + +import android.annotation.Nullable; +import android.os.SystemClock; +import android.util.Slog; + +/** + * The base class of all KernelUidCpuTimeReaders. + * + * This class is NOT designed to be thread-safe or accessed by more than one caller (due to + * the nature of {@link #readDelta(Callback)}). + */ +public abstract class KernelUidCpuTimeReaderBase { + protected static final boolean DEBUG = false; + // Throttle interval in milliseconds + private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L; + + private final String TAG = this.getClass().getSimpleName(); + private long mLastTimeReadMs = Long.MIN_VALUE; + private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL; + + // A generic Callback interface (used by readDelta) to be extended by subclasses. + public interface Callback { + } + + public void readDelta(@Nullable T cb) { + if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) { + if (DEBUG) { + Slog.d(TAG, "Throttle"); + } + return; + } + readDeltaImpl(cb); + mLastTimeReadMs = SystemClock.elapsedRealtime(); + } + + protected abstract void readDeltaImpl(@Nullable T cb); + + public void setThrottleInterval(long throttleInterval) { + if (throttleInterval >= 0) { + mThrottleInterval = throttleInterval; + } + } +} diff --git a/core/java/com/android/internal/os/MediaPowerCalculator.java b/core/java/com/android/internal/os/MediaPowerCalculator.java new file mode 100644 index 0000000000000000000000000000000000000000..a35c1341905bfe569db609c6721d907e0a313418 --- /dev/null +++ b/core/java/com/android/internal/os/MediaPowerCalculator.java @@ -0,0 +1,61 @@ +/* + * 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 com.android.internal.os; + +import android.os.BatteryStats; + +/** + * A {@link PowerCalculator} to calculate power consumed by audio and video hardware. + * + * Also see {@link PowerProfile#POWER_AUDIO} and {@link PowerProfile#POWER_VIDEO}. + */ +public class MediaPowerCalculator extends PowerCalculator { + private static final int MS_IN_HR = 1000 * 60 * 60; + private final double mAudioAveragePowerMa; + private final double mVideoAveragePowerMa; + + public MediaPowerCalculator(PowerProfile profile) { + mAudioAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_AUDIO); + mVideoAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_VIDEO); + } + + @Override + public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + // Calculate audio power usage, an estimate based on the average power routed to different + // components like speaker, bluetooth, usb-c, earphone, etc. + final BatteryStats.Timer audioTimer = u.getAudioTurnedOnTimer(); + if (audioTimer == null) { + app.audioTimeMs = 0; + app.audioPowerMah = 0; + } else { + final long totalTime = audioTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; + app.audioTimeMs = totalTime; + app.audioPowerMah = (totalTime * mAudioAveragePowerMa) / MS_IN_HR; + } + + // Calculate video power usage. + final BatteryStats.Timer videoTimer = u.getVideoTurnedOnTimer(); + if (videoTimer == null) { + app.videoTimeMs = 0; + app.videoPowerMah = 0; + } else { + final long totalTime = videoTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; + app.videoTimeMs = totalTime; + app.videoPowerMah = (totalTime * mVideoAveragePowerMa) / MS_IN_HR; + } + } +} diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index f4436d383a219a8a631aee353cf6fb2928d6bb38..246a50f6ceac848ba11b95d639d1b098adcbfb4a 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -38,17 +38,12 @@ import java.util.HashMap; */ public class PowerProfile { - /** - * No power consumption, or accounted for elsewhere. - */ - public static final String POWER_NONE = "none"; - - /** + /* * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode. * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should * be zero on devices that can go into full CPU power collapse even when a wake * lock is held. Otherwise, this is the power consumption in addition to - * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity. + * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity. * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters * and cores. * @@ -84,7 +79,6 @@ public class PowerProfile { // Updated power constants. These are not estimated, they are real world // currents and voltages for the underlying bluetooth and wifi controllers. // - public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle"; public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx"; public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx"; @@ -104,7 +98,7 @@ public class PowerProfile { public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE = "modem.controller.voltage"; - /** + /** * Power consumption when GPS is on. */ public static final String POWER_GPS_ON = "gps.on"; @@ -117,6 +111,7 @@ public class PowerProfile { /** * Power consumption when Bluetooth driver is on. + * * @deprecated */ @Deprecated @@ -124,6 +119,7 @@ public class PowerProfile { /** * Power consumption when Bluetooth driver is transmitting/receiving. + * * @deprecated */ @Deprecated @@ -131,11 +127,16 @@ public class PowerProfile { /** * Power consumption when Bluetooth driver gets an AT command. + * * @deprecated */ @Deprecated public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at"; + /** + * Power consumption when screen is in doze/ambient/always-on mode, including backlight power. + */ + public static final String POWER_AMBIENT_DISPLAY = "ambient.on"; /** * Power consumption when screen is on, not including the backlight power. @@ -167,13 +168,13 @@ public class PowerProfile { * Power consumed by the audio hardware when playing back audio content. This is in addition * to the CPU power, probably due to a DSP and / or amplifier. */ - public static final String POWER_AUDIO = "dsp.audio"; + public static final String POWER_AUDIO = "audio"; /** * Power consumed by any media hardware when playing back video content. This is in addition * to the CPU power, probably due to a DSP. */ - public static final String POWER_VIDEO = "dsp.video"; + public static final String POWER_VIDEO = "video"; /** * Average power consumption when camera flashlight is on. @@ -405,6 +406,7 @@ public class PowerProfile { /** * Returns the number of memory bandwidth buckets defined in power_profile.xml, or a * default value if the subsystem has no recorded value. + * * @return the number of memory bandwidth buckets. */ public int getNumElements(String key) { @@ -419,7 +421,8 @@ public class PowerProfile { /** * Returns the average current in mA consumed by the subsystem, or the given * default value if the subsystem has no recorded value. - * @param type the subsystem type + * + * @param type the subsystem type * @param defaultValue the value to return if the subsystem has no recorded value. * @return the average current in milliAmps. */ @@ -435,19 +438,21 @@ public class PowerProfile { /** * Returns the average current in mA consumed by the subsystem + * * @param type the subsystem type * @return the average current in milliAmps. */ public double getAveragePower(String type) { return getAveragePowerOrDefault(type, 0); } - + /** * Returns the average current in mA consumed by the subsystem for the given level. - * @param type the subsystem type + * + * @param type the subsystem type * @param level the level of power at which the subsystem is running. For instance, the - * signal strength of the cell network between 0 and 4 (if there are 4 bars max.) - * If there is no data for multiple levels, the level is ignored. + * signal strength of the cell network between 0 and 4 (if there are 4 bars max.) + * If there is no data for multiple levels, the level is ignored. * @return the average current in milliAmps. */ public double getAveragePower(String type, int level) { @@ -470,6 +475,7 @@ public class PowerProfile { /** * Returns the battery capacity, if available, in milli Amp Hours. If not available, * it returns zero. + * * @return the battery capacity in mAh */ public double getBatteryCapacity() { diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index 32b580c2d277ffbd04d56482368debf7a10bfd80..9f2434e97d7a2f8449bb359f4075da4256110533 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -27,6 +27,7 @@ import android.text.TextUtils; import android.util.Log; import android.webkit.WebViewFactory; import android.webkit.WebViewFactoryProvider; +import android.webkit.WebViewLibraryLoader; import java.io.DataOutputStream; import java.io.File; @@ -71,7 +72,8 @@ class WebViewZygoteInit { } @Override - protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { + protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName, + String cacheKey) { Log.i(TAG, "Beginning package preload"); // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that // our children will reuse the same classloader instead of creating their own. @@ -80,6 +82,10 @@ class WebViewZygoteInit { ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader( packagePath, libsPath, cacheKey); + // Load the native library using WebViewLibraryLoader to share the RELRO data with other + // processes. + WebViewLibraryLoader.loadNativeLibrary(loader, libFileName); + // Add the APK to the Zygote's list of allowed files for children. String[] packageList = TextUtils.split(packagePath, File.pathSeparator); for (String packageEntry : packageList) { diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index a32fb4316d12ef27563f4b5ce3c46f055ba61ae6..cd83c57f60f94788595fea4d8a9d35dc0dd58fe8 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -155,7 +155,7 @@ class ZygoteConnection { if (parsedArgs.preloadPackage != null) { handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs, - parsedArgs.preloadPackageCacheKey); + parsedArgs.preloadPackageLibFileName, parsedArgs.preloadPackageCacheKey); return null; } @@ -290,7 +290,8 @@ class ZygoteConnection { return mSocketOutStream; } - protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) { + protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName, + String cacheKey) { throw new RuntimeException("Zyogte does not support package preloading"); } @@ -402,10 +403,24 @@ class ZygoteConnection { String appDataDir; /** - * Whether to preload a package, with the package path in the remainingArgs. + * The APK path of the package to preload, when using --preload-package. */ String preloadPackage; + + /** + * The native library path of the package to preload, when using --preload-package. + */ String preloadPackageLibs; + + /** + * The filename of the native library to preload, when using --preload-package. + */ + String preloadPackageLibFileName; + + /** + * The cache key under which to enter the preloaded package into the classloader cache, + * when using --preload-package. + */ String preloadPackageCacheKey; /** @@ -571,6 +586,7 @@ class ZygoteConnection { } else if (arg.equals("--preload-package")) { preloadPackage = args[++curArg]; preloadPackageLibs = args[++curArg]; + preloadPackageLibFileName = args[++curArg]; preloadPackageCacheKey = args[++curArg]; } else if (arg.equals("--preload-default")) { preloadDefault = true; diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java index adad0362eca9c9f986b93b12c26281eaae9a3f10..bfe3780b64483ef9f57835f65034fdef4e72ae52 100644 --- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java +++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Pair; import android.util.StatsLog; +import android.view.WindowManager.LayoutParams; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -103,13 +104,27 @@ public class MetricsLoggerWrapper { StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN); } - public static void logAppOverlayEnter(int uid, String packageName, boolean usingAlertWindow) { - StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, usingAlertWindow, - StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED); + public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) { + if (changed) { + if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) { + StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, true, + StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED); + } else if (!usingAlertWindow){ + StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, false, + StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED); + } + } } - public static void logAppOverlayExit(int uid, String packageName, boolean usingAlertWindow) { - StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, usingAlertWindow, - StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED); + public static void logAppOverlayExit(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) { + if (changed) { + if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) { + StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, true, + StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED); + } else if (!usingAlertWindow){ + StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, false, + StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED); + } + } } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 50c9d6c69ac96088f570bf3d98be7c95529d0fcf..528888fbaab6ae7ec7b7cc76c8fbf503f678510d 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2464,6 +2464,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { decor.setSystemUiVisibility( decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); } + if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) { + int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1); + if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT + || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) { + throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: " + + a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode)); + } + params.layoutInDisplayCutoutMode = mode; + } if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { diff --git a/core/java/com/android/internal/textservice/LazyIntToIntMap.java b/core/java/com/android/internal/textservice/LazyIntToIntMap.java new file mode 100644 index 0000000000000000000000000000000000000000..ca9936c5fcdabce79058ab86cd211827106bf5a8 --- /dev/null +++ b/core/java/com/android/internal/textservice/LazyIntToIntMap.java @@ -0,0 +1,67 @@ +/* + * 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 com.android.internal.textservice; + +import android.annotation.NonNull; +import android.util.SparseIntArray; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.function.IntUnaryOperator; + +/** + * Simple int-to-int key-value-store that is to be lazily initialized with the given + * {@link IntUnaryOperator}. + */ +@VisibleForTesting +public final class LazyIntToIntMap { + + private final SparseIntArray mMap = new SparseIntArray(); + + @NonNull + private final IntUnaryOperator mMappingFunction; + + /** + * @param mappingFunction int to int mapping rules to be (lazily) evaluated + */ + public LazyIntToIntMap(@NonNull IntUnaryOperator mappingFunction) { + mMappingFunction = mappingFunction; + } + + /** + * Deletes {@code key} and associated value. + * @param key key to be deleted + */ + public void delete(int key) { + mMap.delete(key); + } + + /** + * @param key key associated with the value + * @return value associated with the {@code key}. If this is the first time to access + * {@code key}, then {@code mappingFunction} passed to the constructor will be evaluated + */ + public int get(int key) { + final int index = mMap.indexOfKey(key); + if (index >= 0) { + return mMap.valueAt(index); + } + final int value = mMappingFunction.applyAsInt(key); + mMap.append(key, value); + return value; + } +} diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java index 518cf41ee37a8678b1da7f5ab4a3715632467ebe..b962d4fc9f5e442454c1d51ae3dab5208e851715 100644 --- a/core/java/com/android/internal/util/NotificationMessagingUtil.java +++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java @@ -78,6 +78,10 @@ public class NotificationMessagingUtil { return false; } + return isMessaging(sbn); + } + + public boolean isMessaging(StatusBarNotification sbn) { Class style = sbn.getNotification().getNotificationStyle(); if (Notification.MessagingStyle.class.equals(style)) { return true; diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index e08caa8e199d5ed0d215d1bb11296d716749167d..28291aefd036e2d3bb0f5d864c8b4bcb1df4699c 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; import android.os.Handler; -import android.os.LocaleList; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -65,7 +64,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; private static final int DO_CLOSE_CONNECTION = 150; private static final int DO_COMMIT_CONTENT = 160; - private static final int DO_REPORT_LANGUAGE_HINT = 170; @GuardedBy("mLock") @Nullable @@ -219,10 +217,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { callback)); } - public void reportLanguageHint(@NonNull LocaleList languageHint) { - dispatchMessage(obtainMessageO(DO_REPORT_LANGUAGE_HINT, languageHint)); - } - void dispatchMessage(Message msg) { // If we are calling this from the main thread, then we can call // right through. Otherwise, we need to send the message to the @@ -583,16 +577,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } - case DO_REPORT_LANGUAGE_HINT: { - final LocaleList languageHint = (LocaleList) msg.obj; - final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "reportLanguageHint on inactive InputConnection"); - return; - } - ic.reportLanguageHint(languageHint); - return; - } } Log.w(TAG, "Unhandled message code: " + msg.what); } diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index e69a87ffc1dd3213a10a1714709105792705ddbd..c22799179b72ebb7ee023142ba08e978eabe4641 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -17,7 +17,6 @@ package com.android.internal.view; import android.os.Bundle; -import android.os.LocaleList; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -79,6 +78,4 @@ import com.android.internal.view.IInputContextCallback; void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts, int sec, IInputContextCallback callback); - - void reportLanguageHint(in LocaleList languageHint); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 02822869f4212d9556725df4c3a6b80c7ae9ad04..9ed1ffba68972ce31209e116c6f6518f06a55d9e 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -80,7 +80,7 @@ interface IInputMethodManager { boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index); InputMethodSubtype getCurrentInputMethodSubtype(); boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype); - boolean switchToLastInputMethod(in IBinder token); + boolean switchToPreviousInputMethod(in IBinder token); boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme); boolean shouldOfferSwitchingToNextInputMethod(in IBinder token); void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 34be598dd615768069bc570ad99bb248de5d5d68..5b65bbe1e90e6aa49630be44d0dfa25a87ad3caf 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; -import android.os.LocaleList; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; @@ -620,14 +619,6 @@ public class InputConnectionWrapper implements InputConnection { return result; } - @AnyThread - public void reportLanguageHint(@NonNull LocaleList languageHint) { - try { - mIInputContext.reportLanguageHint(languageHint); - } catch (RemoteException e) { - } - } - @AnyThread private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) { return (mMissingMethods & methodFlag) == methodFlag; diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index d7b91325c961bb2cd2aaea626fa36000da579f0a..e9472fa0f464fb3ba125736e9f88184e5d7fbd0a 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -41,7 +41,8 @@ import com.android.internal.R; public final class RotationPolicy { private static final String TAG = "RotationPolicy"; private static final int CURRENT_ROTATION = -1; - private static final int NATURAL_ROTATION = Surface.ROTATION_0; + + public static final int NATURAL_ROTATION = Surface.ROTATION_0; private RotationPolicy() { } diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java index 6dff8b41eeb49206747096741cdd9f4ecb94bbb0..bf3e8d56b6bc3a0cbc883df130da6ca6c3a40031 100644 --- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java +++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java @@ -47,6 +47,7 @@ import com.android.internal.util.Preconditions; */ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener { + private static final int ITEM_LAYOUT = com.android.internal.R.layout.cascading_menu_item_layout; @Retention(RetentionPolicy.SOURCE) @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT}) @@ -348,7 +349,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey */ private void showMenu(@NonNull MenuBuilder menu) { final LayoutInflater inflater = LayoutInflater.from(mContext); - final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly); + final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly, ITEM_LAYOUT); // Apply "force show icon" setting. There are 3 cases: // (1) This is the top level menu and icon spacing is forced. Add spacing. diff --git a/core/java/com/android/internal/view/menu/MenuAdapter.java b/core/java/com/android/internal/view/menu/MenuAdapter.java index 2834d39a4f98501209ae7e671b9655f2b23593d9..5bc981b4fc93595b5f11786a2b6863cb1a98423e 100644 --- a/core/java/com/android/internal/view/menu/MenuAdapter.java +++ b/core/java/com/android/internal/view/menu/MenuAdapter.java @@ -23,8 +23,6 @@ import android.widget.BaseAdapter; import java.util.ArrayList; public class MenuAdapter extends BaseAdapter { - static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout; - MenuBuilder mAdapterMenu; private int mExpandedIndex = -1; @@ -32,11 +30,14 @@ public class MenuAdapter extends BaseAdapter { private boolean mForceShowIcon; private final boolean mOverflowOnly; private final LayoutInflater mInflater; + private final int mItemLayoutRes; - public MenuAdapter(MenuBuilder menu, LayoutInflater inflater, boolean overflowOnly) { + public MenuAdapter(MenuBuilder menu, LayoutInflater inflater, boolean overflowOnly, + int itemLayoutRes) { mOverflowOnly = overflowOnly; mInflater = inflater; mAdapterMenu = menu; + mItemLayoutRes = itemLayoutRes; findExpandedIndex(); } @@ -78,7 +79,7 @@ public class MenuAdapter extends BaseAdapter { public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { - convertView = mInflater.inflate(ITEM_LAYOUT, parent, false); + convertView = mInflater.inflate(mItemLayoutRes, parent, false); } final int currGroupId = getItem(position).getGroupId(); diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 67dc81af5895ca17d3626646290aa7c3d0a9fcb1..48485e0d95f6d8670c79c050cc65d64162af6926 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -37,6 +37,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; +import android.view.ViewConfiguration; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -753,8 +754,7 @@ public class MenuBuilder implements Menu { private void setShortcutsVisibleInner(boolean shortcutsVisible) { mShortcutsVisible = shortcutsVisible && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS - && mResources.getBoolean( - com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent); + && ViewConfiguration.get(mContext).shouldShowMenuShortcutsWhenKeyboardPresent(); } /** diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java index 445379b1d9f49cb760e8f609f9d7370f5dd2043a..d618f6758faafd50ff716c42f89130e7a72b8f8f 100644 --- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java +++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java @@ -43,6 +43,7 @@ import com.android.internal.util.Preconditions; */ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, OnItemClickListener, MenuPresenter, OnKeyListener { + private static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout; private final Context mContext; @@ -116,7 +117,7 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On mMenu = menu; mOverflowOnly = overflowOnly; final LayoutInflater inflater = LayoutInflater.from(context); - mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly); + mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly, ITEM_LAYOUT); mPopupStyleAttr = popupStyleAttr; mPopupStyleRes = popupStyleRes; diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index d3fc644c2341aa927920c6b705bba745d1cea58a..5a06f7fe19906b76d617c27060bc179518419566 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -65,9 +65,12 @@ interface ILockSettings { // {@code ServiceSpecificException} may be thrown to signal an error, which caller can // convert to {@code RecoveryManagerException}. void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList); + void initRecoveryServiceWithSigFile(in String rootCertificateAlias, + in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile); KeyChainSnapshot getKeyChainSnapshot(); byte[] generateAndStoreKey(String alias); String generateKey(String alias); + String importKey(String alias, in byte[] keyBytes); String getKey(String alias); void removeKey(String alias); void setSnapshotCreatedPendingIntent(in PendingIntent intent); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e871003a6489d02246a07732295f6b7067df9763..7eb2f38f52873b44db9cc63a2fb0c25372b2a6f1 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -42,6 +42,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; +import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.google.android.collect.Lists; @@ -120,8 +121,6 @@ public class LockPatternUtils { @Deprecated public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; - public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; - public final static String LOCKOUT_ATTEMPT_TIMEOUT_MS = "lockscreen.lockoutattempttimeoutmss"; public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; @Deprecated @@ -164,6 +163,7 @@ public class LockPatternUtils { private ILockSettings mLockSettingsService; private UserManager mUserManager; private final Handler mHandler; + private final SparseLongArray mLockoutDeadlines = new SparseLongArray(); /** * Use {@link TrustManager#isTrustUsuallyManaged(int)}. @@ -1237,8 +1237,7 @@ public class LockPatternUtils { // enforces the deadline. Since we cannot store settings for the FRP user, don't. return deadline; } - setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId); - setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, timeoutMs, userId); + mLockoutDeadlines.put(userId, deadline); return deadline; } @@ -1248,22 +1247,13 @@ public class LockPatternUtils { * enter a pattern. */ public long getLockoutAttemptDeadline(int userId) { - long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId); - final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId); + final long deadline = mLockoutDeadlines.get(userId, 0L); final long now = SystemClock.elapsedRealtime(); if (deadline < now && deadline != 0) { // timeout expired - setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId); - setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId); + mLockoutDeadlines.put(userId, 0); return 0L; } - - if (deadline > (now + timeoutMs)) { - // device was rebooted, set new deadline - deadline = now + timeoutMs; - setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId); - } - return deadline; } diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java new file mode 100644 index 0000000000000000000000000000000000000000..3d3a3d07b216320255730312a9101afce23b2246 --- /dev/null +++ b/core/java/com/android/server/net/BaseNetdEventCallback.java @@ -0,0 +1,38 @@ +/* + * 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 com.android.server.net; + +import android.net.INetdEventCallback; + +/** + * Base {@link INetdEventCallback} that provides no-op + * implementations which can be overridden. + * + * @hide + */ +public class BaseNetdEventCallback extends INetdEventCallback.Stub { + @Override + public void onDnsEvent(String hostname, String[] ipAddresses, + int ipAddressesCount, long timestamp, int uid) { + // default no-op + } + + @Override + public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) { + // default no-op + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index e9787a9bf5eb0c5fe7be61821d10bae7fba89046..c710eae7815f9665aaaff9f7c4ed08fe4f798901 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -191,6 +191,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 32c8f76acba2362ebb786ef40d69c1f16ec149f8..2ab188edcda77c31642f3680985dd1a0306bf969 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,6 +123,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1347,6 +1348,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp index 6fd4abf27baa3e8450c198e180c52a31d57bd6f0..d6496cdf499afca9291eb2d9a5bee2f07b8b1e92 100644 --- a/core/jni/android/graphics/AnimatedImageDrawable.cpp +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -132,13 +132,13 @@ static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlon // Java's LOOP_INFINITE relies on this being the same. static_assert(SkCodec::kRepetitionCountInfinite == -1); -static jint AnimatedImageDrawable_nGetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { +static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast(nativePtr); return drawable->getRepetitionCount(); } -static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, - jint loopCount) { +static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint loopCount) { auto* drawable = reinterpret_cast(nativePtr); drawable->setRepetitionCount(loopCount); } @@ -213,6 +213,12 @@ static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, drawable->markInvisible(); } +static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jboolean mirrored) { + auto* drawable = reinterpret_cast(nativePtr); + drawable->setStagingMirrored(mirrored); +} + static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nCreate", "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate }, { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, @@ -223,11 +229,12 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop }, - { "nGetLoopCount", "(J)I", (void*) AnimatedImageDrawable_nGetLoopCount }, - { "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount }, + { "nGetRepeatCount", "(J)I", (void*) AnimatedImageDrawable_nGetRepeatCount }, + { "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount }, { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, { "nMarkInvisible", "(J)V", (void*) AnimatedImageDrawable_nMarkInvisible }, + { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored }, }; int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 48aef4a8b320b428f6ee0476811c534ac382d9a4..c7ad2a47c3677c129769b25e49d273e22632de3a 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -51,6 +51,18 @@ struct NativeFamilyBuilder { std::vector axes; }; +static inline NativeFamilyBuilder* toNativeBuilder(jlong ptr) { + return reinterpret_cast(ptr); +} + +static inline FontFamilyWrapper* toFamily(jlong ptr) { + return reinterpret_cast(ptr); +} + +template static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast(ptr); +} + static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, jint variant) { NativeFamilyBuilder* builder; if (langs != nullptr) { @@ -59,15 +71,14 @@ static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, j } else { builder = new NativeFamilyBuilder(minikin::registerLocaleList(""), variant); } - return reinterpret_cast(builder); + return toJLong(builder); } static jlong FontFamily_create(jlong builderPtr) { if (builderPtr == 0) { return 0; } - std::unique_ptr builder( - reinterpret_cast(builderPtr)); + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); if (builder->fonts.empty()) { return 0; } @@ -76,17 +87,23 @@ static jlong FontFamily_create(jlong builderPtr) { if (family->getCoverage().length() == 0) { return 0; } - return reinterpret_cast(new FontFamilyWrapper(std::move(family))); + return toJLong(new FontFamilyWrapper(std::move(family))); } -static void FontFamily_abort(jlong builderPtr) { - NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - delete builder; +static void releaseBuilder(jlong builderPtr) { + delete toNativeBuilder(builderPtr); } -static void FontFamily_unref(jlong familyPtr) { - FontFamilyWrapper* family = reinterpret_cast(familyPtr); - delete family; +static jlong FontFamily_getBuilderReleaseFunc() { + return toJLong(&releaseBuilder); +} + +static void releaseFamily(jlong familyPtr) { + delete toFamily(familyPtr); +} + +static jlong FontFamily_getFamilyReleaseFunc() { + return toJLong(&releaseFamily); } static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp&& data, int ttcIndex, @@ -175,7 +192,7 @@ static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr, jobject font, jint ttcIndex, jint weight, jint isItalic) { NPE_CHECK_RETURN_ZERO(env, font); - NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); const void* fontPtr = env->GetDirectBufferAddress(font); if (fontPtr == NULL) { ALOGE("addFont failed to create font, buffer invalid"); @@ -204,8 +221,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jassetMgr); NPE_CHECK_RETURN_ZERO(env, jpath); - NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -217,44 +234,50 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) { - NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); builder->axes.push_back({static_cast(tag), value}); } /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gFontFamilyMethods[] = { - { "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder }, - { "nCreateFamily", "(J)J", (void*)FontFamily_create }, - { "nAbort", "(J)V", (void*)FontFamily_abort }, - { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, - { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, - { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", + { "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder }, + { "nCreateFamily", "(J)J", (void*)FontFamily_create }, + { "nGetBuilderReleaseFunc", "()J", (void*)FontFamily_getBuilderReleaseFunc }, + { "nGetFamilyReleaseFunc", "()J", (void*)FontFamily_getFamilyReleaseFunc }, + { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, + { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFontWeightStyle }, { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z", (void*)FontFamily_addFontFromAssetManager }, diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index d67c0b018efbef7467227115cf46e58d430289cc..34ec365c0bdc64043215006de9485aa5b372b0ef 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -28,8 +28,16 @@ using namespace android; +static inline Typeface* toTypeface(jlong ptr) { + return reinterpret_cast(ptr); +} + +template static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast(ptr); +} + static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) { - Typeface* family = reinterpret_cast(familyHandle); + Typeface* family = toTypeface(familyHandle); Typeface* face = Typeface::createRelative(family, (Typeface::Style)style); // TODO: the following logic shouldn't be necessary, the above should always succeed. // Try to find the closest matching font, using the standard heuristic @@ -39,13 +47,12 @@ static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandl for (int i = 0; NULL == face && i < 4; i++) { face = Typeface::createRelative(family, (Typeface::Style)i); } - return reinterpret_cast(face); + return toJLong(face); } static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance, jint weight, jboolean italic) { - Typeface* baseTypeface = reinterpret_cast(nativeInstance); - return reinterpret_cast(Typeface::createAbsolute(baseTypeface, weight, italic)); + return toJLong(Typeface::createAbsolute(toTypeface(nativeInstance), weight, italic)); } static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle, @@ -60,30 +67,30 @@ static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlon AxisHelper axis(env, axisObject); variations.push_back(minikin::FontVariation(axis.getTag(), axis.getStyleValue())); } - Typeface* baseTypeface = reinterpret_cast(familyHandle); - Typeface* result = Typeface::createFromTypefaceWithVariation(baseTypeface, variations); - return reinterpret_cast(result); + return toJLong(Typeface::createFromTypefaceWithVariation(toTypeface(familyHandle), variations)); } static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) { - Typeface* family = reinterpret_cast(familyHandle); - Typeface* face = Typeface::createWithDifferentBaseWeight(family, weight); - return reinterpret_cast(face); + return toJLong(Typeface::createWithDifferentBaseWeight(toTypeface(familyHandle), weight)); +} + +static void releaseFunc(jlong ptr) { + delete toTypeface(ptr); } -static void Typeface_unref(JNIEnv* env, jobject obj, jlong faceHandle) { - Typeface* face = reinterpret_cast(faceHandle); - delete face; +// CriticalNative +static jlong Typeface_getReleaseFunc() { + return toJLong(&releaseFunc); } -static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) { - Typeface* face = reinterpret_cast(faceHandle); - return face->fAPIStyle; +// CriticalNative +static jint Typeface_getStyle(jlong faceHandle) { + return toTypeface(faceHandle)->fAPIStyle; } -static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) { - Typeface* face = reinterpret_cast(faceHandle); - return face->fStyle.weight(); +// CriticalNative +static jint Typeface_getWeight(jlong faceHandle) { + return toTypeface(faceHandle)->fStyle.weight(); } static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray, @@ -95,17 +102,16 @@ static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArr FontFamilyWrapper* family = reinterpret_cast(families[i]); familyVec.emplace_back(family->family); } - return reinterpret_cast( - Typeface::createFromFamilies(std::move(familyVec), weight, italic)); + return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic)); } -static void Typeface_setDefault(JNIEnv *env, jobject, jlong faceHandle) { - Typeface* face = reinterpret_cast(faceHandle); - Typeface::setDefault(face); +// CriticalNative +static void Typeface_setDefault(jlong faceHandle) { + Typeface::setDefault(toTypeface(faceHandle)); } static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) { - Typeface* face = reinterpret_cast(faceHandle); + Typeface* face = toTypeface(faceHandle); const std::unordered_set& tagSet = face->fFontCollection->getSupportedTags(); const size_t length = tagSet.size(); if (length == 0) { @@ -131,7 +137,7 @@ static const JNINativeMethod gTypefaceMethods[] = { { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J", (void*)Typeface_createFromTypefaceWithVariation }, { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias }, - { "nativeUnref", "(J)V", (void*)Typeface_unref }, + { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc }, { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight }, { "nativeCreateFromArray", "([JII)J", diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp index 5eecd9cf76ef867fd605f89e573af26b008209d8..09adc824e5202c94f8d75711d18f2f50227ad51d 100644 --- a/core/jni/android/graphics/YuvToJpegEncoder.cpp +++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp @@ -23,16 +23,28 @@ YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { } +struct ErrorMgr { + struct jpeg_error_mgr pub; + jmp_buf jmp; +}; + +void error_exit(j_common_ptr cinfo) { + ErrorMgr* err = (ErrorMgr*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(err->jmp, 1); +} + bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, int height, int* offsets, int jpegQuality) { jpeg_compress_struct cinfo; - jpeg_error_mgr err; + ErrorMgr err; skjpeg_destination_mgr sk_wstream(stream); - cinfo.err = jpeg_std_error(&err); - err.error_exit = skjpeg_error_exit; - jmp_buf jmp; - if (setjmp(jmp)) { + cinfo.err = jpeg_std_error(&err.pub); + err.pub.error_exit = error_exit; + + if (setjmp(err.jmp)) { + jpeg_destroy_compress(&cinfo); return false; } jpeg_create_compress(&cinfo); @@ -47,6 +59,8 @@ bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + return true; } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6dedb4e7d6bd8691428bd03d08ddc..49a24a30f77edd31e129121f21bfefdf2caac7f5 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7738d849be39cfe8970284561121d61877b36299 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,157 @@ +/* + * 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. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" +#include "utils/Trace.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str()); + + std::unique_ptr apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str()); + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast(ptr); + std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 61d5031e16ed47eda2bdb38c40d438e632044ac3..0c06be21023b38255c560d2882d1ccae36be5a8a 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -62,6 +62,18 @@ struct fields_t { jmethodID rect_constructor; jmethodID face_constructor; jmethodID point_constructor; + jfieldID face_sm_degree; + jfieldID face_sm_score; + jfieldID face_blink_detected; + jfieldID face_gaze_angle; + jfieldID face_updown_dir; + jfieldID face_leftright_dir; + jfieldID face_roll_dir; + jfieldID face_leye_blink; + jfieldID face_reye_blink; + jfieldID face_left_right_gaze; + jfieldID face_top_bottom_gaze; + jfieldID face_recognised; }; static fields_t fields; @@ -100,6 +112,7 @@ private: jclass mFaceClass; // strong reference to Face class jclass mRectClass; // strong reference to Rect class jclass mPointClass; // strong reference to Point class + bool mIsExtendedFace; Mutex mLock; /* @@ -151,8 +164,16 @@ JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, mCameraJClass = (jclass)env->NewGlobalRef(clazz); mCamera = camera; - jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); - mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + jclass extendedfaceClazz = env->FindClass("com/qualcomm/qti/camera/ExtendedFace"); + if (NULL != extendedfaceClazz) { + mFaceClass = (jclass) env->NewGlobalRef(extendedfaceClazz); + mIsExtendedFace = true; + } else { + env->ExceptionClear(); + jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); + mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + mIsExtendedFace = false; + } jclass rectClazz = env->FindClass("android/graphics/Rect"); mRectClass = (jclass) env->NewGlobalRef(rectClazz); @@ -400,7 +421,6 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(rect, fields.rect_top, metadata->faces[i].rect[1]); env->SetIntField(rect, fields.rect_right, metadata->faces[i].rect[2]); env->SetIntField(rect, fields.rect_bottom, metadata->faces[i].rect[3]); - env->SetObjectField(face, fields.face_rect, rect); env->SetIntField(face, fields.face_score, metadata->faces[i].score); @@ -429,6 +449,21 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(mouth, fields.point_y, metadata->faces[i].mouth[1]); env->SetObjectField(face, fields.face_mouth, mouth); env->DeleteLocalRef(mouth); + + if (mIsExtendedFace) { + env->SetIntField(face, fields.face_sm_degree, metadata->faces[i].smile_degree); + env->SetIntField(face, fields.face_sm_score, metadata->faces[i].smile_score); + env->SetIntField(face, fields.face_blink_detected, metadata->faces[i].blink_detected); + env->SetIntField(face, fields.face_recognised, metadata->faces[i].face_recognised); + env->SetIntField(face, fields.face_gaze_angle, metadata->faces[i].gaze_angle); + env->SetIntField(face, fields.face_updown_dir, metadata->faces[i].updown_dir); + env->SetIntField(face, fields.face_leftright_dir, metadata->faces[i].leftright_dir); + env->SetIntField(face, fields.face_roll_dir, metadata->faces[i].roll_dir); + env->SetIntField(face, fields.face_leye_blink, metadata->faces[i].leye_blink); + env->SetIntField(face, fields.face_reye_blink, metadata->faces[i].reye_blink); + env->SetIntField(face, fields.face_left_right_gaze, metadata->faces[i].left_right_gaze); + env->SetIntField(face, fields.face_top_bottom_gaze, metadata->faces[i].top_bottom_gaze); + } } env->DeleteLocalRef(face); @@ -466,6 +501,56 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM } } +static void android_hardware_Camera_setLongshot(JNIEnv *env, jobject thiz, jboolean enable) +{ + ALOGV("setLongshot"); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if ( enable ) { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_ON, 0, 0); + } else { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_OFF, 0, 0); + } + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "enabling longshot mode failed"); + } +} + +static void android_hardware_Camera_sendHistogramData(JNIEnv *env, jobject thiz) + { + ALOGV("sendHistogramData" ); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_SEND_DATA, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "send histogram data failed"); + } + } + static void android_hardware_Camera_setHistogramMode(JNIEnv *env, jobject thiz, jboolean mode) + { + ALOGV("setHistogramMode: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set histogram mode failed"); + } + } void JNICameraContext::addCallbackBuffer( JNIEnv *env, jbyteArray cbb, int msgType) { @@ -789,7 +874,25 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t context->setCallbackMode(env, installed, manualBuffer); } -static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, jint msgType) { +static void android_hardware_Camera_setMetadataCb(JNIEnv *env, jobject thiz, jboolean mode) +{ + ALOGV("setMetadataCb: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_METADATA_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_METADATA_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set metadata mode failed"); + } +} + +static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, int msgType) { ALOGV("addCallbackBuffer: 0x%x", msgType); JNICameraContext* context = reinterpret_cast(env->GetLongField(thiz, fields.context)); @@ -1022,7 +1125,7 @@ static void android_hardware_Camera_enableFocusMoveCallback(JNIEnv *env, jobject //------------------------------------------------- static const JNINativeMethod camMethods[] = { - { "getNumberOfCameras", + { "_getNumberOfCameras", "()I", (void *)android_hardware_Camera_getNumberOfCameras }, { "_getCameraInfo", @@ -1067,6 +1170,18 @@ static const JNINativeMethod camMethods[] = { { "native_takePicture", "(I)V", (void *)android_hardware_Camera_takePicture }, + { "native_setHistogramMode", + "(Z)V", + (void *)android_hardware_Camera_setHistogramMode }, + { "native_setMetadataCb", + "(Z)V", + (void *)android_hardware_Camera_setMetadataCb }, + { "native_sendHistogramData", + "()V", + (void *)android_hardware_Camera_sendHistogramData }, + { "native_setLongshot", + "(Z)V", + (void *)android_hardware_Camera_setLongshot }, { "native_setParameters", "(Ljava/lang/String;)V", (void *)android_hardware_Camera_setParameters }, @@ -1145,6 +1260,27 @@ int register_android_hardware_Camera(JNIEnv *env) { "android/graphics/Point", "y", "I", &fields.point_y}, }; + field extendedfacefields_to_find[] = { + { "com/qualcomm/qti/camera/ExtendedFace", "rect", "Landroid/graphics/Rect;", &fields.face_rect }, + { "com/qualcomm/qti/camera/ExtendedFace", "score", "I", &fields.face_score }, + { "com/qualcomm/qti/camera/ExtendedFace", "id", "I", &fields.face_id }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftEye", "Landroid/graphics/Point;", &fields.face_left_eye }, + { "com/qualcomm/qti/camera/ExtendedFace", "rightEye", "Landroid/graphics/Point;", &fields.face_right_eye }, + { "com/qualcomm/qti/camera/ExtendedFace", "mouth", "Landroid/graphics/Point;", &fields.face_mouth }, + { "com/qualcomm/qti/camera/ExtendedFace", "smileDegree", "I", &fields.face_sm_degree }, + { "com/qualcomm/qti/camera/ExtendedFace", "smileScore", "I", &fields.face_sm_score }, + { "com/qualcomm/qti/camera/ExtendedFace", "blinkDetected", "I", &fields.face_blink_detected }, + { "com/qualcomm/qti/camera/ExtendedFace", "faceRecognized", "I", &fields.face_recognised }, + { "com/qualcomm/qti/camera/ExtendedFace", "gazeAngle", "I", &fields.face_gaze_angle }, + { "com/qualcomm/qti/camera/ExtendedFace", "updownDir", "I", &fields.face_updown_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftrightDir", "I", &fields.face_leftright_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "rollDir", "I", &fields.face_roll_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "leyeBlink", "I", &fields.face_leye_blink }, + { "com/qualcomm/qti/camera/ExtendedFace", "reyeBlink", "I", &fields.face_reye_blink }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftrightGaze", "I", &fields.face_left_right_gaze }, + { "com/qualcomm/qti/camera/ExtendedFace", "topbottomGaze", "I", &fields.face_top_bottom_gaze }, + }; + find_fields(env, fields_to_find, NELEM(fields_to_find)); jclass clazz = FindClassOrDie(env, "android/hardware/Camera"); @@ -1164,6 +1300,12 @@ int register_android_hardware_Camera(JNIEnv *env) return -1; } + clazz = env->FindClass("com/qualcomm/qti/camera/ExtendedFace"); + if (NULL != clazz) { + fields.face_constructor = env->GetMethodID(clazz, "", "()V"); + find_fields(env, extendedfacefields_to_find, NELEM(extendedfacefields_to_find)); + } + // Register native functions return RegisterMethodsOrDie(env, "android/hardware/Camera", camMethods, NELEM(camMethods)); } diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index 2ec30b852e815002446d9a6dc9a6eeff9df20cda..6b5e45c47a50e36f5603f576282c3cb716fbfc61 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -85,7 +85,7 @@ static inline audio_format_t audioFormatToNative(int audioFormat) case ENCODING_AAC_ELD: return AUDIO_FORMAT_AAC_ELD; case ENCODING_AAC_XHE: - return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC + return AUDIO_FORMAT_AAC_XHE; case ENCODING_AC4: return AUDIO_FORMAT_AC4; // case ENCODING_E_AC3_JOC: // FIXME Not defined on the native side yet @@ -102,6 +102,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_EVRCWB; case ENCODING_EVRC_NW: return AUDIO_FORMAT_EVRCNW; + case ENCODING_E_AC3_JOC: + return AUDIO_FORMAT_E_AC3_JOC; case ENCODING_DEFAULT: return AUDIO_FORMAT_DEFAULT; default: @@ -166,6 +168,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_EVRC_WB; case AUDIO_FORMAT_EVRCNW: return ENCODING_EVRC_NW; + case AUDIO_FORMAT_E_AC3_JOC: + return ENCODING_E_AC3_JOC; case AUDIO_FORMAT_DEFAULT: return ENCODING_DEFAULT; default: diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 61a22c144f1b1f76fc7b38c916356b3d34931864..6456fe622f98cae2fff39468d5b5754a9c3a1b22 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -282,7 +282,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job // compute the frame count size_t frameCount; - if (audio_is_linear_pcm(format)) { + if (audio_has_proportional_frames(format)) { const size_t bytesPerSample = audio_bytes_per_sample(format); frameCount = buffSizeInBytes / (channelCount * bytesPerSample); } else { diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index d18c1726918124f170192c52cdc1f6b11a1e6643..5e2cd4001d235be2cb44b310f2474287974f4d4e 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -28,9 +28,9 @@ #include #include -#include #include #include +#include #include #include #include diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 683b4c490ec39f124616c5d9bbcf7a63e3c4c75e..8be6ed8c415d86a594895bfe9272b10a8fd13e14 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1851 +1,1449 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, 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. -*/ +/* + * Copyright 2006, 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 ATRACE_TAG ATRACE_TAG_RESOURCES #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" #include "utils/misc.h" #include "utils/String8.h" +#include "utils/Trace.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } - -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + ATRACE_NAME("AssetManager::SetApkAssets"); - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ATRACE_NAME("AssetManager::SetConfiguration"); + + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; -} +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset_dir = + assetmanager->OpenDir(path_utf8.c_str()); + if (asset_dir == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return nullptr; + } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } + const size_t file_count = asset_dir->getFileCount(); - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + for (size_t i = 0; i < file_count; i++) { + jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, i, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str()); + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str()); + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - ssize_t block = -1; - Res_value value; +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - return static_cast(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} - -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } - - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index d17993a72aaf82fb61e2af8912255177cc743a83..1b206fd8abdddc5ed8fcce28dedf3e6f02fb1c20 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -35,8 +35,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 8fee7baa398f940e0718ca93815339b8520e1f4e..b43bcd7410603383621e2dfa2187dd4209cd8948 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -46,6 +46,7 @@ static struct { jmethodID dispatchInputEvent; jmethodID dispatchBatchedInputEventPending; + jmethodID dispatchMotionEventInfo; } gInputEventReceiverClassInfo; @@ -77,6 +78,8 @@ private: bool mBatchedInputEventPending; int mFdEvents; Vector mFinishQueue; + int mLastMotionEventType = -1; + int mLastTouchMoveNum = -1; void setFdEvents(int events); @@ -235,10 +238,34 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool skipCallbacks = false; for (;;) { uint32_t seq; + int motionEventType = -1; + int touchMoveNum = -1; + bool flag = false; + InputEvent* inputEvent; int32_t displayId; status_t status = mInputConsumer.consume(&mInputEventFactory, - consumeBatches, frameTime, &seq, &inputEvent, &displayId); + consumeBatches, frameTime, &seq, &inputEvent, &displayId, + &motionEventType, &touchMoveNum, &flag); + + if (!receiverObj.get()) { + receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); + if (!receiverObj.get()) { + ALOGW("channel '%s' ~ Receiver object was finalized " + "without being disposed.", getInputChannelName().c_str()); + return DEAD_OBJECT; + } + } + + if (flag && ((mLastMotionEventType != motionEventType) || + (mLastTouchMoveNum != touchMoveNum))) { + env->CallVoidMethod(receiverObj.get(), + gInputEventReceiverClassInfo.dispatchMotionEventInfo, motionEventType, touchMoveNum); + mLastMotionEventType = motionEventType; + mLastTouchMoveNum = touchMoveNum; + flag = false; + } + if (status) { if (status == WOULD_BLOCK) { if (!skipCallbacks && !mBatchedInputEventPending @@ -426,7 +453,8 @@ int register_android_view_InputEventReceiver(JNIEnv* env) { "dispatchInputEvent", "(ILandroid/view/InputEvent;I)V"); gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V"); - + gInputEventReceiverClassInfo.dispatchMotionEventInfo = GetMethodIDOrDie(env, + gInputEventReceiverClassInfo.clazz, "dispatchMotionEventInfo", "(II)V"); return res; } diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index f5c09fd316de9fac6a236d279a1caba8303ba678..f70cf07b9535fa70ac6ab716afecedff4455a0f1 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -546,12 +546,16 @@ public: } }; -static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfacePtr) { +static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfacePtr, + jboolean isWideColorGamut) { RenderNode* rootNode = reinterpret_cast(rootNodePtr); sp surface(reinterpret_cast(surfacePtr)); ContextFactory factory; RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); proxy->loadSystemProperties(); + if (isWideColorGamut) { + proxy->setWideGamut(true); + } proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); proxy->initialize(surface); // Shadows can't be used via this interface, so just set the light source @@ -620,7 +624,7 @@ static const JNINativeMethod gSurfaceMethods[] = { {"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled}, // HWUI context - {"nHwuiCreate", "(JJ)J", (void*) hwui::create }, + {"nHwuiCreate", "(JJZ)J", (void*) hwui::create }, {"nHwuiSetSurface", "(JJ)V", (void*) hwui::setSurface }, {"nHwuiDraw", "(J)V", (void*) hwui::draw }, {"nHwuiDestroy", "(J)V", (void*) hwui::destroy }, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 0ef5445afffc4c4d4103e8837f231152a64eed6c..8ca506254f74002ab189867a3d2eaed6ff2befb3 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -846,6 +846,14 @@ static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong transa transaction->setOverrideScalingMode(ctrl, scalingMode); } +static void nativeDestroyInTransaction(JNIEnv* env, jclass clazz, + jlong transactionObj, + jlong nativeObject) { + auto transaction = reinterpret_cast(transactionObj); + auto ctrl = reinterpret_cast(nativeObject); + transaction->destroySurface(ctrl); +} + static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { auto ctrl = reinterpret_cast(nativeObject); return javaObjectForIBinder(env, ctrl->getHandle()); @@ -997,6 +1005,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSeverChildren } , {"nativeSetOverrideScalingMode", "(JJI)V", (void*)nativeSetOverrideScalingMode }, + {"nativeDestroy", "(JJ)V", + (void*)nativeDestroyInTransaction }, {"nativeGetHandle", "(J)Landroid/os/IBinder;", (void*)nativeGetHandle }, {"nativeScreenshotToBuffer", diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 13e0e4a6bdb76815d309ff2276513c4caa1849f5..b614c891b9f830a95b3951ba68385eb50b212cc2 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -984,6 +984,10 @@ static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, j Properties::enableRTAnimations = enable; } +static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) { + Properties::debuggingEnabled = enable; +} + // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- @@ -1092,6 +1096,7 @@ static const JNINativeMethod gMethods[] = { { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText }, { "nHackySetRTAnimationsEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled }, + { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled }, }; static JavaVM* mJvm = nullptr; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index d6fe5687edb9d739877163afd28f4e752585e156..d05be2ca787ba185cb2d73c7d75e23eeb293ff99 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -488,6 +488,8 @@ void SetThreadName(const char* thread_name) { if (errno != 0) { ALOGW("Unable to set the name of current thread to '%s': %s", buf, strerror(errno)); } + // Update base::logging default tag. + android::base::SetDefaultTag(buf); } // The list of open zygote file descriptors. @@ -685,10 +687,10 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra // Make it easier to debug audit logs by setting the main thread's name to the // nice name rather than "app_process". - if (se_info_c_str == NULL && is_system_server) { + if (se_name_c_str == NULL && is_system_server) { se_name_c_str = "system_server"; } - if (se_info_c_str != NULL) { + if (se_name_c_str != NULL) { SetThreadName(se_name_c_str); } diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a57368a0c605b79e040a7abaa28c7..2c1e3579eb9281b6ebba278c717e42bc74ffc578 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/core/proto/README.md b/core/proto/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d2b89a52e9274f999c7d355620a960a1c4d2552b --- /dev/null +++ b/core/proto/README.md @@ -0,0 +1,41 @@ +Conventions for the protos in this directory: + +1. As in the rest of Android, use 4 spaces to indent instead of 2. + +1. For protos based on Java files, use the same package as the Java file. For + example, `com.android.server.thing` instead of `com.android.server.thing.proto`. + +1. If the proto describes the top level output of dumpsys, it should contain + `Dump`. This makes it easy to understand that the proto is the dumpsys output + of a certain service, not the data structure of that service, e.g. + `WindowManagerServiceDumpProto` vs `WindowManagerServiceDumpProto`. + + * Inner messages whose containing messages have the `Proto` suffix do not + need to have a `Proto` suffix. E.g: + +``` +message FooProto { + message Bar { + ... + } +} +``` + + vs + +``` +message FooProto { + message BarProto { + ... + } +} +``` + +1. If the proto represents the structure of an object, it should have `Proto` as + its suffix. Please also include the full package path of the original object + as a comment to the proto message. + +1. Include units in the field names. For example, `screen_time_ms` vs + `screen_time`, or `file_size_bytes` or `file_size_mebibytes` vs `file_size`. + +1. Leave field numbers 50,000 - 100,000 reserved for OEMs. diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto new file mode 100644 index 0000000000000000000000000000000000000000..9e459e6c1e5c17ec9cc5ea0b6d064f0a57eee856 --- /dev/null +++ b/core/proto/android/bluetooth/enums.proto @@ -0,0 +1,43 @@ +/* + * Copyright 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.bluetooth; + +option java_outer_classname = "BluetoothProtoEnums"; +option java_multiple_files = true; + +// Bluetooth connection states. +enum ConnectionStateEnum { + CONNECTION_STATE_DISCONNECTED = 0; + CONNECTION_STATE_CONNECTING = 1; + CONNECTION_STATE_CONNECTED = 2; + CONNECTION_STATE_DISCONNECTING = 3; +} + +// Bluetooth Adapter Enable and Disable Reasons +enum EnableDisableReasonEnum { + ENABLE_DISABLE_REASON_UNSPECIFIED = 0; + ENABLE_DISABLE_REASON_APPLICATION_REQUEST = 1; + ENABLE_DISABLE_REASON_AIRPLANE_MODE = 2; + ENABLE_DISABLE_REASON_DISALLOWED = 3; + ENABLE_DISABLE_REASON_RESTARTED = 4; + ENABLE_DISABLE_REASON_START_ERROR = 5; + ENABLE_DISABLE_REASON_SYSTEM_BOOT = 6; + ENABLE_DISABLE_REASON_CRASH = 7; + ENABLE_DISABLE_REASON_USER_SWITCH = 8; + ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9; +} \ No newline at end of file diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto index 834ecdee6a9ff57617da3538b5dc88cb0d0bccc3..74b47d2424b2fa951938b99497416e412be5e7a2 100644 --- a/core/proto/android/content/configuration.proto +++ b/core/proto/android/content/configuration.proto @@ -35,9 +35,9 @@ message ConfigurationProto { optional uint32 mnc = 3; repeated LocaleProto locales = 4; optional uint32 screen_layout = 5; - optional uint32 hdr_color_mode = 6; - optional uint32 wide_color_gamut = 7; - optional uint32 touchscreen = 8; + optional uint32 color_mode = 6; + optional uint32 touchscreen = 7; + optional uint32 keyboard = 8; optional uint32 keyboard_hidden = 9; optional uint32 hard_keyboard_hidden = 10; optional uint32 navigation = 11; diff --git a/core/proto/android/os/backtrace.proto b/core/proto/android/os/backtrace.proto new file mode 100644 index 0000000000000000000000000000000000000000..ba81386bb4aa2c02c8e94b4d0f1ebf37a0770011 --- /dev/null +++ b/core/proto/android/os/backtrace.proto @@ -0,0 +1,36 @@ +/* + * 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; + +option java_multiple_files = true; + +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + +message BackTraceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + message Stack { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 pid = 1; + optional string dump = 2; + // Time it took to dump the stacktrace. + optional int64 dump_duration_ns = 3; + } + repeated Stack traces = 1; +} diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index 9915174020d76173d4517544b70ebf61233a7ff1..f468143256a990c472329214e52a0f25b2861a34 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -641,9 +641,7 @@ message UidProto { message Job { option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional string name = 1 [ - (android.privacy).dest = DEST_EXPLICIT - ]; + optional string name = 1; // Job times aren't apportioned. optional TimerProto total = 2; optional TimerProto background = 3; @@ -654,9 +652,7 @@ message UidProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; // Job name. - optional string name = 1 [ - (android.privacy).dest = DEST_EXPLICIT - ]; + optional string name = 1; message ReasonCount { option (android.msg_privacy).dest = DEST_AUTOMATIC; @@ -801,9 +797,7 @@ message UidProto { message Sync { option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional string name = 1 [ - (android.privacy).dest = DEST_EXPLICIT - ]; + optional string name = 1; // Sync times aren't apportioned. optional TimerProto total = 2; optional TimerProto background = 3; @@ -842,9 +836,7 @@ message UidProto { message Wakelock { option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional string name = 1 [ - (android.privacy).dest = DEST_EXPLICIT - ]; + optional string name = 1; // Full wakelocks keep the screen on. Based on // PowerManager.SCREEN_BRIGHT_WAKE_LOCK (deprecated in API 13) and @@ -876,9 +868,7 @@ message UidProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; // Wakeup alarm name. - optional string name = 1 [ - (android.privacy).dest = DEST_EXPLICIT - ]; + optional string name = 1; // Only includes counts when screen-off (& on battery). optional int32 count = 2; } diff --git a/core/proto/android/os/data.proto b/core/proto/android/os/data.proto new file mode 100644 index 0000000000000000000000000000000000000000..c06f318a7312aed8b257aa2cc499a4a99d7da6e4 --- /dev/null +++ b/core/proto/android/os/data.proto @@ -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. + */ + +syntax = "proto2"; +option java_multiple_files = true; + +package android.os; + +// This file contains protobuf definitions used in incidentd directly. +// The top level proto message must be used as a new SectionType in +// incidentd. + +// Output of SECTION_GZIP section type, which reads a file, gzip it and attached +// in incident report as a proto field, example is LAST_KMSG. +// NOTE the content in the file must not contain sensitive PII otherwise +// implement it with fine-grained proto definition. +message GZippedFileProto { + optional string filename = 1; + + optional bytes gzipped_data = 2; +} diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto index fe9b7ac01291a817349b35f96f6383e90991718f..aa99ac75cbdfb9e3a0158e228dee9bfe8dd6f7df 100644 --- a/core/proto/android/os/enums.proto +++ b/core/proto/android/os/enums.proto @@ -56,6 +56,17 @@ enum BatteryStatusEnum { BATTERY_STATUS_FULL = 5; } +// These constants are defined in hardware/interfaces/thermal/1.0/types.hal +// They are primarily used by android/os/HardwarePropertiesManager.java. +// Any change to the types in the thermal hal should be made here as well. +enum TemperatureTypeEnum { + TEMPERATURE_TYPE_UKNOWN = -1; + TEMPERATURE_TYPE_CPU = 0; + TEMPERATURE_TYPE_GPU = 1; + TEMPERATURE_TYPE_BATTERY = 2; + TEMPERATURE_TYPE_SKIN = 3; +} + // Wakelock types, primarily used by android/os/PowerManager.java. enum WakeLockLevelEnum { // NOTE: Wake lock levels were previously defined as a bit field, except diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 9a53b89ffe306248f0d55a3e3020db420c20a811..4657dc478059cbbe7dbe1aa09cc8c53d7299dc15 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -17,9 +17,11 @@ syntax = "proto2"; option java_multiple_files = true; +import "frameworks/base/core/proto/android/os/backtrace.proto"; import "frameworks/base/core/proto/android/os/batterytype.proto"; import "frameworks/base/core/proto/android/os/cpufreq.proto"; import "frameworks/base/core/proto/android/os/cpuinfo.proto"; +import "frameworks/base/core/proto/android/os/data.proto"; import "frameworks/base/core/proto/android/os/kernelwake.proto"; import "frameworks/base/core/proto/android/os/pagetypeinfo.proto"; import "frameworks/base/core/proto/android/os/procrank.proto"; @@ -52,9 +54,8 @@ import "frameworks/base/libs/incident/proto/android/section.proto"; package android.os; -// privacy field options must not be set at this level because all -// the sections are able to be controlled and configured by section ids. -// Instead privacy field options need to be configured in each section proto message. +// Privacy tag can be marked to override UNSET messages so generic +// message type can be handled case by case, e.g. GZippedFileProto. message IncidentProto { reserved 1001; @@ -115,6 +116,22 @@ message IncidentProto { (section).args = "LOG_ID_KERNEL" ]; + // Stack dumps + optional android.os.BackTraceProto native_traces = 1200 [ + (section).type = SECTION_TOMBSTONE, + (section).args = "native" + ]; + + optional android.os.BackTraceProto hal_traces = 1201 [ + (section).type = SECTION_TOMBSTONE, + (section).args = "hal" + ]; + + optional android.os.BackTraceProto java_traces = 1202 [ + (section).type = SECTION_TOMBSTONE, + (section).args = "java" + ]; + // Linux services optional ProcrankProto procrank = 2000 [ (section).type = SECTION_NONE, // disable procrank until figure out permission @@ -151,6 +168,12 @@ message IncidentProto { (section).args = "/sys/class/power_supply/bms/battery_type" ]; + optional GZippedFileProto last_kmsg = 2007 [ + (section).type = SECTION_GZIP, + (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg", + (privacy).dest = DEST_AUTOMATIC + ]; + // System Services optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [ (section).type = SECTION_DUMPSYS, @@ -208,37 +231,37 @@ message IncidentProto { (section).args = "procstats --proto" ]; - optional com.android.server.am.proto.ActivityStackSupervisorProto activities = 3012 [ + optional com.android.server.am.proto.ActivityManagerServiceDumpActivitiesProto activities = 3012 [ (section).type = SECTION_DUMPSYS, (section).args = "activity --proto activities" ]; - optional com.android.server.am.proto.BroadcastProto broadcasts = 3013 [ + optional com.android.server.am.proto.ActivityManagerServiceDumpBroadcastsProto broadcasts = 3013 [ (section).type = SECTION_DUMPSYS, (section).args = "activity --proto broadcasts" ]; - optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [ + optional com.android.server.am.proto.ActivityManagerServiceDumpServicesProto amservices = 3014 [ (section).type = SECTION_DUMPSYS, (section).args = "activity --proto service" ]; - optional com.android.server.am.proto.ProcessesProto amprocesses = 3015 [ + optional com.android.server.am.proto.ActivityManagerServiceDumpProcessesProto amprocesses = 3015 [ (section).type = SECTION_DUMPSYS, (section).args = "activity --proto processes" ]; - optional com.android.server.AlarmManagerServiceProto alarm = 3016 [ + optional com.android.server.AlarmManagerServiceDumpProto alarm = 3016 [ (section).type = SECTION_DUMPSYS, (section).args = "alarm --proto" ]; - optional com.android.server.wm.proto.WindowManagerServiceProto window = 3017 [ + optional com.android.server.wm.proto.WindowManagerServiceDumpProto window = 3017 [ (section).type = SECTION_DUMPSYS, (section).args = "window --proto" ]; - optional com.android.server.am.proto.MemInfoProto meminfo = 3018 [ + optional com.android.server.am.proto.MemInfoDumpProto meminfo = 3018 [ (section).type = SECTION_DUMPSYS, (section).args = "meminfo -a --proto" ]; @@ -257,4 +280,7 @@ message IncidentProto { (section).type = SECTION_DUMPSYS, (section).args = "usb --proto" ]; + + // Reserved for OEMs. + extensions 50000 to 100000; } diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index 9752d3b0a3d86de581454f91ef446ee0f8fb83c3..d7ba421ec3c699321dbc6bd0a6aaac98eaf73f02 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -432,10 +432,10 @@ message GlobalSettingsProto { optional SettingProto show_restart_in_crash_dialog = 351 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto show_mute_in_crash_dialog = 352 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingsProto show_zen_upgrade_notification = 354 [ (android.privacy).dest = DEST_AUTOMATIC ]; - + optional SettingsProto app_auto_restriction_enabled = 359 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Please insert fields in the same order as in // frameworks/base/core/java/android/provider/Settings.java. - // Next tag = 359; + // Next tag = 360; } message SecureSettingsProto { diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 788d90127c3d79ad83c5abab52c6dcdaab40b1bf..3b9150f44abc325a82cc053431a68061fcb1b94c 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -40,16 +40,22 @@ option java_multiple_files = true; message ActivityManagerServiceProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; - optional ActivityStackSupervisorProto activities = 1; + optional ActivityManagerServiceDumpActivitiesProto activities = 1; - optional BroadcastProto broadcasts = 2; + optional ActivityManagerServiceDumpBroadcastsProto broadcasts = 2; - optional ActiveServicesProto services = 3; + optional ActivityManagerServiceDumpServicesProto services = 3; - optional ProcessesProto processes = 4; + optional ActivityManagerServiceDumpProcessesProto processes = 4; } // "dumpsys activity --proto activities" +message ActivityManagerServiceDumpActivitiesProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional ActivityStackSupervisorProto activity_stack_supervisor = 1; +} + message ActivityStackSupervisorProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -58,6 +64,9 @@ message ActivityStackSupervisorProto { optional KeyguardControllerProto keyguard_controller = 3; optional int32 focused_stack_id = 4; optional .com.android.server.wm.proto.IdentifierProto resumed_activity = 5; + // Whether or not the home activity is the recents activity. This is needed for the CTS tests to + // know what activity types to check for when invoking splitscreen multi-window. + optional bool is_home_recents_component = 6; } /* represents ActivityStackSupervisor.ActivityDisplay */ @@ -118,7 +127,7 @@ message KeyguardControllerProto { } // "dumpsys activity --proto broadcasts" -message BroadcastProto { +message ActivityManagerServiceDumpBroadcastsProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; repeated ReceiverListProto receiver_list = 1; @@ -199,7 +208,7 @@ message BroadcastQueueProto { repeated BroadcastSummary historical_broadcasts_summary = 6; } -message MemInfoProto { +message MemInfoDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional int64 uptime_duration_ms = 1; @@ -403,6 +412,12 @@ message StickyBroadcastProto { } // "dumpsys activity --proto service" +message ActivityManagerServiceDumpServicesProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional ActiveServicesProto active_services = 1; +} + message ActiveServicesProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -582,7 +597,7 @@ message IntentBindRecordProto { } // TODO: "dumpsys activity --proto processes" -message ProcessesProto { +message ActivityManagerServiceDumpProcessesProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; repeated ProcessRecordProto procs = 1; diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto index d1c5db66a8414d7117cc05e1831dafb45f7f14b8..53e3ba965120f79073ee9d5bdea43a6d017d09c8 100644 --- a/core/proto/android/server/alarmmanagerservice.proto +++ b/core/proto/android/server/alarmmanagerservice.proto @@ -28,7 +28,7 @@ package com.android.server; option java_multiple_files = true; // next ID: 43 -message AlarmManagerServiceProto { +message AlarmManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional int64 current_time = 1; @@ -220,6 +220,8 @@ message ConstantsProto { optional int64 allow_while_idle_long_duration_ms = 5; // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE. optional int64 allow_while_idle_whitelist_duration_ms = 6; + // Maximum alarm recurrence interval. + optional int64 max_interval_duration_ms = 7; } // A com.android.server.AlarmManagerService.FilterStats object. diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 69abed30234dbeb8ed7c2e62f531eccdc3485a23..9193129def11d673c2d158e774f4cdb42c36fa2b 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -614,7 +614,6 @@ message JobStatusDumpProto { CONSTRAINT_DEADLINE = 5; CONSTRAINT_IDLE = 6; CONSTRAINT_CONNECTIVITY = 7; - CONSTRAINT_APP_NOT_IDLE = 8; CONSTRAINT_CONTENT_TRIGGER = 9; CONSTRAINT_DEVICE_NOT_DOZING = 10; } diff --git a/core/proto/android/server/statlogger.proto b/core/proto/android/server/statlogger.proto index 2ae526a599663ce154268f1215da86af4a7c2dc0..2b7daea00a6c9904c2312d66d626f513c398935e 100644 --- a/core/proto/android/server/statlogger.proto +++ b/core/proto/android/server/statlogger.proto @@ -24,10 +24,13 @@ import "frameworks/base/libs/incident/proto/android/privacy.proto"; // Dump from StatLogger. message StatLoggerProto { - option (.android.msg_privacy).dest = DEST_EXPLICIT; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; message Event { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 eventId = 1; + // Labels are hard-coded in Android framework code. optional string label = 2; optional int32 count = 3; optional int64 total_duration_micros = 4; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c11058a2210729487e642d7c5b0be98d71709ea4..9598f24b6ff1101bdfa8dd21093af47ee6826ddf 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -32,7 +32,7 @@ package com.android.server.wm.proto; option java_multiple_files = true; -message WindowManagerServiceProto { +message WindowManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional WindowManagerPolicyProto policy = 1; @@ -295,7 +295,6 @@ message WindowStateProto { optional bool animating_exit = 14; repeated WindowStateProto child_windows = 15; optional .android.graphics.RectProto surface_position = 16; - optional .android.graphics.RectProto shown_position = 17; optional int32 requested_width = 18; optional int32 requested_height = 19; optional int32 view_visibility = 20; diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto index d96953ee9ded2428421817f7aa202a1135e3fa0a..96a90bfe63daa69330a019752679bb58f6f63af9 100644 --- a/core/proto/android/server/windowmanagertrace.proto +++ b/core/proto/android/server/windowmanagertrace.proto @@ -48,5 +48,5 @@ message WindowManagerTraceProto { /* where the trace originated */ optional string where = 2; - optional WindowManagerServiceProto window_manager_service = 3; + optional WindowManagerServiceDumpProto window_manager_service = 3; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7d068430c2f1216732bf7fdd81cff9e4c0bd5a57..cd32777956d5f8dfe0cf40b06e5f80fce9c08233 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -56,6 +56,7 @@ + @@ -87,6 +88,7 @@ + @@ -2998,7 +3000,18 @@ settings app. This permission cannot be granted to third-party apps.

    Protection level: signature --> - + + + + @hide + @SystemApi --> @@ -3339,13 +3352,6 @@ - - - @@ -3836,6 +3842,15 @@ + + + - + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml b/core/res/res/drawable/ic_alert_window_layer.xml similarity index 71% rename from packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml rename to core/res/res/drawable/ic_alert_window_layer.xml index c8c857c7ef0cae19e4ce63045a60d995a11b8039..15931b80791804999775bf4477973ac0780df92c 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml +++ b/core/res/res/drawable/ic_alert_window_layer.xml @@ -1,5 +1,5 @@ - + android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/> + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml b/core/res/res/drawable/ic_camera.xml similarity index 56% rename from packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml rename to core/res/res/drawable/ic_camera.xml index fda87612582ed75f9095663e11cbd9fd64bdcf69..2921a689ef8a73059c435178ea0dedc7d6f2cccd 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml +++ b/core/res/res/drawable/ic_camera.xml @@ -1,5 +1,5 @@ - + android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" + android:fillColor="#FFFFFF"/> + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml index 78cce586ac18de0efba503c76858913727df1802..915f5fb96c019f91ae125da2cb3cbebfa3fb42ed 100644 --- a/core/res/res/drawable/ic_corp_badge.xml +++ b/core/res/res/drawable/ic_corp_badge.xml @@ -1,12 +1,37 @@ + + + android:viewportWidth="20" + android:viewportHeight="20"> + + android:fillColor="#fcfcfc" + android:pathData="M 10 0 C 15.5228474983 0 20 4.47715250169 20 10 C 20 15.5228474983 15.5228474983 20 10 20 C 4.47715250169 20 0 15.5228474983 0 10 C 0 4.47715250169 4.47715250169 0 10 0 Z" /> - + android:strokeColor="#e8eaed" + android:strokeWidth="0.25" + android:pathData="M 10 0.12 C 15.4565733283 0.12 19.88 4.54342667167 19.88 10 C 19.88 15.4565733283 15.4565733283 19.88 10 19.88 C 4.54342667167 19.88 0.12 15.4565733283 0.12 10 C 0.12 4.54342667167 4.54342667167 0.12 10 0.12 Z" /> + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_corp_badge_case.xml b/core/res/res/drawable/ic_corp_badge_case.xml index 2d11ee61eabb14679b3b99d5a2651d510cf225a5..1cd995eea171432458381e4a57a903d1f4e4023d 100644 --- a/core/res/res/drawable/ic_corp_badge_case.xml +++ b/core/res/res/drawable/ic_corp_badge_case.xml @@ -5,5 +5,5 @@ android:viewportHeight="20.0"> + android:fillColor="#1A73E8"/> diff --git a/core/res/res/drawable/ic_corp_badge_color.xml b/core/res/res/drawable/ic_corp_badge_color.xml index b6c79696c0224cf57a4c2f6154c44abfe6ae4e37..4aef7d007590d800f37d0079755c2207c88a718d 100644 --- a/core/res/res/drawable/ic_corp_badge_color.xml +++ b/core/res/res/drawable/ic_corp_badge_color.xml @@ -1,3 +1,4 @@ + + android:viewportWidth="64" + android:viewportHeight="64"> + - + android:pathData="M 42 42 L 58 42 L 58 58 L 42 58 L 42 42 Z" /> + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_corp_icon_badge_color.xml b/core/res/res/drawable/ic_corp_icon_badge_color.xml index 3bc4e67ec8df0bd286953d82b6d24d23eb9df57b..6dba2779bc5309a9d4e186ab08b92c6cb1459cc1 100644 --- a/core/res/res/drawable/ic_corp_icon_badge_color.xml +++ b/core/res/res/drawable/ic_corp_icon_badge_color.xml @@ -1,3 +1,4 @@ + + android:width="64dp" + android:height="64dp" + android:viewportWidth="64" + android:viewportHeight="64"> + + - + android:pathData="M 0 0 H 64 V 64 H 0 V 0 Z" /> + \ No newline at end of file diff --git a/core/res/res/drawable/ic_corp_icon_badge_shadow.xml b/core/res/res/drawable/ic_corp_icon_badge_shadow.xml index a546cdd29a8e2d765af9524fc4bc4555645eb2cd..f33ed1f010be545c52875dd58517de575a7c7c46 100644 --- a/core/res/res/drawable/ic_corp_icon_badge_shadow.xml +++ b/core/res/res/drawable/ic_corp_icon_badge_shadow.xml @@ -1,3 +1,4 @@ + + android:width="64dp" + android:height="64dp" + android:viewportWidth="64" + android:viewportHeight="64"> + + + + + android:fillColor="#000000" + android:fillAlpha="0.06" + android:strokeAlpha="0.06" + android:strokeWidth="1" + android:pathData="M62,50.75a12,12,0,1,1-12-12,12,12,0,0,1,12,12" /> - + android:fillColor="#000000" + android:fillAlpha="0.06" + android:strokeAlpha="0.06" + android:strokeWidth="1" + android:pathData="M62,50.25a12,12,0,1,1-12-12,12,12,0,0,1,12,12" /> + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml b/core/res/res/drawable/ic_mic.xml similarity index 59% rename from packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml rename to core/res/res/drawable/ic_mic.xml index acaa9b1c2be8ced2c582154e86cfd1772ff2ce63..3212330278aaecfb199332a1b3602a608c182a78 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml +++ b/core/res/res/drawable/ic_mic.xml @@ -1,5 +1,5 @@ - + android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" + android:fillColor="#FFFFFF"/> + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml b/core/res/res/drawable/ic_qs_battery_saver.xml similarity index 65% rename from packages/SystemUI/res/drawable/ic_qs_signal_0.xml rename to core/res/res/drawable/ic_qs_battery_saver.xml index b78d3bf1c35de7199db2082dc6cd348906805d77..89b25690a5db3a6a63b2335187f3ef1ba470ee8f 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml +++ b/core/res/res/drawable/ic_qs_battery_saver.xml @@ -1,5 +1,5 @@ + + + + + + + + + + diff --git a/core/res/res/layout/cascading_menu_item_layout.xml b/core/res/res/layout/cascading_menu_item_layout.xml new file mode 100644 index 0000000000000000000000000000000000000000..7d186359f736de48856febaf8b044f2385feea19 --- /dev/null +++ b/core/res/res/layout/cascading_menu_item_layout.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 20bdf3fe8fa38f24cfb7851b0ecd9a63e3c54b0c..3196d000f191a073c061da19809faf66bf12ccb1 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> - + + + + + + + diff --git a/core/res/res/values-as-watch/strings.xml b/core/res/res/values-as-watch/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..1c91d10ac22a94cad56a21474603ba1d535e2dd7 --- /dev/null +++ b/core/res/res/values-as-watch/strings.xml @@ -0,0 +1,26 @@ + + + + + + + "ছেন্সৰসমূহ" + diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..5ab2fa64a840ede4a8557a7d73a407895c993ce1 --- /dev/null +++ b/core/res/res/values-as/strings.xml @@ -0,0 +1,2006 @@ + + + + + "বা." + "কে. বি." + "এম. বি." + "জি. বি." + "টি. বি." + "পি. বি." + "%1$s %2$s" + "<শিৰোনামবিহীন>" + "(কোনো ফ\'ন নম্বৰ নাই)" + "অজ্ঞাত" + "ভইচমেইল" + "MSISDN1" + "সংযোগৰ সমস্যা বা MMI ক\'ড মান্য নহয়।" + "কেৱল ফিক্সড ডায়েলিং নম্বৰৰ বাবে কার্য সীমাবদ্ধ কৰা হৈছে।" + "আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিংসমূহ সলনি কৰিব নোৱাৰি।" + "সেৱা সক্ষম কৰা হ\'ল।" + "সেৱা সক্ষম কৰা হ\'ল:" + "সেৱা অক্ষম কৰা হ\'ল।" + "পঞ্জীকৰণ সফল হ\'ল।" + "সফলভাৱে মচা হ\'ল৷" + "ভুল পাছৱৰ্ড৷" + "MMI সম্পূৰ্ণ হ’ল।" + "আপুনি লিখা পুৰণি পিনটো শুদ্ধ নহয়।" + "আপুনি লিখা PUKটো শুদ্ধ নহয়।" + "আপুনি লিখা পিনবিলাক মিলা নাই।" + "৪টাৰ পৰা ৮টা সংখ্যাযুক্ত এটা পিন লিখক।" + "৮টা সংখ্যা বা তাতকৈ বেছি সংখ্যাৰ এটা PUK লিখক।" + "আপোনাৰ ছিমটো PUK ক\'ডেৰে লক কৰা আছে। PUK ক\'ড লিখি ইয়াক আনলক কৰক।" + "ছিম কার্ড আনলক কৰিবলৈ PUK2 দিয়ক৷" + "অসফল, ছিম/RUIM লক সক্ষম কৰক।" + + ছিম লক হোৱাৰ পূৰ্বে আপোনাৰ %dটা প্ৰয়াস বাকী আছে। + ছিম লক হোৱাৰ পূৰ্বে আপোনাৰ %dটা প্ৰয়াস বাকী আছে। + + "IMEI" + "MEID" + "অন্তৰ্গামী কলাৰ আইডি" + "বহিৰ্গামী কলাৰ আইডি" + "সংযোজিত লাইন আইডি" + "সংযোজিত লাইন আইডিৰ সীমাবদ্ধতা" + "কল ফৰৱাৰ্ডিং" + "কল অপেক্ষাৰত" + "কল অৱৰোধ কৰা সুবিধা" + "পাছৱর্ড সলনি কৰা" + "পিন সলনি কৰা" + "বর্তমান কল কৰা নম্বৰ" + "বর্তমান কল কৰা নম্বৰ সীমিত কৰা হ\'ল" + "ত্ৰিপক্ষীয় কলিং" + "আমনিদায়ক কল প্ৰত্যাখ্যান" + "কল কৰা নম্বৰত ডেলিভাৰী" + "অসুবিধা নিদিব" + "কলাৰ আইডি সীমিত কৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হৈছে" + "কলাৰ আইডি সীমিত কৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হৈছে" + "কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই" + "কলাৰ আইডি সীমিত নকৰিবলৈ পূর্বনির্ধাৰণ কৰা হৈছে। পৰৱৰ্তী কল: সীমিত কৰা হোৱা নাই" + "সুবিধা যোগান ধৰা হোৱা নাই।" + "আপুনি কলাৰ আইডি ছেটিং সলনি কৰিব নোৱাৰে।" + "কোনো ডেটা সেৱা নাই" + "জৰুৰীকালীন কল অৱৰোধিত কৰা হৈছে" + "কোনো ভইচ সেৱা নাই" + "কোনো ভইচ/জৰুৰীকালীন সেৱা নাই" + "আপোনাৰ এলেকাত সাময়িকভাৱে ম\'বাইল নেটৱৰ্কটোৱে যোগান ধৰা নাই" + "নেটৱর্ক পাব পৰা নাই" + "বেতাঁৰ সম্প্ৰচাৰ লাভৰ মান উন্নত কৰিবলৈ, ছেটিংসমূহ > নেটৱৰ্ক আৰু ইণ্টাৰনেট > ম\'বাইল নেটৱৰ্কসমূহ > পচন্দৰ নেটৱৰ্কৰ প্ৰকাৰ-লৈ গৈ বাছনি কৰি থোৱা নেটৱৰ্কৰ প্ৰকাৰটো সলনি কৰি চাওক।" + "ৱাই-ফাই কলিং সক্ৰিয় হৈ আছে" + "জৰুৰীকালীন কল কৰিবলৈ নেটৱৰ্কৰ প্ৰয়োজন।" + "সতৰ্কবাণীসমূহ" + "কল ফৰৱাৰ্ডিং" + "জৰুৰীকালীন ক\'লবেক ম\'ড" + "ম\'বাইল ডেটাৰ স্থিতি" + "এছএমএছ বার্তাবোৰ" + "ভইচমেইলৰ বাৰ্তাসমূহ" + "ৱাই-ফাই কলিং" + + + "নেটৱৰ্ক পীয়েৰে TTY ম\'ড FULLলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে" + "নেটৱৰ্ক পীয়েৰে TTY ম\'ড HCOলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে" + "নেটৱৰ্ক পীয়েৰে TTY ম\'ড VCO লৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে" + "নেটৱৰ্ক পীয়েৰে TTY ম\'ড OFFলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে" + "কণ্ঠস্বৰ" + "ডেটা" + "ফেক্স" + "এছএমএছ" + "এছিংক" + "ছিংক" + "পেকেট" + "পেড" + "ৰ\'মিঙৰ সূচক অন হৈ আছে" + "ৰ\'মিঙৰ সূচক অফ আছে" + "ৰ\'মিঙৰ সূচক জিলিকি আছে" + "চুবুৰিৰ বাহিৰত" + "বিল্ডিঙৰ বাহিৰত" + "ৰ\'মিং - পচন্দৰ ছিষ্টেম" + "ৰ\'মিং- উপলব্ধ ছিষ্টেম" + "ৰ\'মিং - মিত্ৰ-গোষ্ঠীৰ অংশীদাৰ" + "ৰ\'মিং- প্ৰিমিয়াম অংশীদাৰ" + "ৰ\'মিং - সেৱা সম্পূর্ণভাৱে কৰ্মক্ষম" + "ৰ\'মিং - সেৱা আংশীকভাৱে কৰ্মক্ষম" + "ৰ\'মিঙৰ বেনাৰ অন অৱস্থাত আছে" + "ৰ\'মিঙৰ বেনাৰ অফ অৱস্থাত আছে" + "সেৱাৰ বাবে অনুসন্ধান কৰি থকা হৈছে" + + + + "ৱাই-ফাইৰ জৰিয়তে কল কৰিবলৈ আৰু বাৰ্তা পঠাবলৈ, প্ৰথমে আপোনাৰ বাহকক আপোনাৰ ডিভাইচটো ছেট আপ কৰিব দিবলৈ কওক। তাৰ পিচত, ছেটিংসমূহলৈ গৈ আকৌ ৱাই-ফাই কলিং অন কৰক। (ত্ৰুটি ক\'ড: %1$s)" + + + "আপোনাৰ বাহকৰ ওচৰত পঞ্জীয়ন কৰক (ত্ৰুটি ক\'ড: %1$s)" + + + "%s" + "%s ৱাই- ফাই কলিং" + + + + + + "ম\'বাইলক অগ্ৰাধিকাৰ দিয়া হৈছে" + + + "{0}: ফৰৱাৰ্ড কৰা নহ\'ল" + "{0}: {1}" + "{0}: {1} {2} ছেকেণ্ডৰ পাছত" + "{0}: ফৰৱাৰ্ড কৰা নহ\'ল" + "{0}: ফৰৱাৰ্ড কৰা নহ\'ল" + "সুবিধাৰ ক\'ড সম্পূর্ণ হ\'ল।" + "সংযোগত সমস্যা হৈছে বা সুবিধাৰ ক\'ড অমান্য।" + "ঠিক আছে" + "ইণ্টাৰনেট সম্পর্কীয় আসোঁৱাহ হ\'ল৷" + "URL বিচাৰি পোৱা নগ\'ল।" + "ছাইট সত্যাপন আঁচনিটো সমৰ্থিত নহয়৷" + "বিশ্বাসযোগ্যতাৰ প্ৰমাণ কৰিব পৰা নগ\'ল।" + "প্ৰ\'ক্সী ছার্ভাৰৰ জৰিয়তে বিশ্বাসযোগ্যতা প্ৰমাণ কৰিব পৰা নগ\'ল।" + "ছার্ভাৰত সংযোগ কৰিব পৰা নগ\'ল।" + "ছার্ভাৰৰ সৈতে যোগাযোগ কৰিব পৰা নগ\'ল। আকৌ চেষ্টা কৰক।" + "ছার্ভাৰৰ লগত সংযোগৰ চেষ্টাৰ সময়সীমা উকলিল৷" + "এই পৃষ্টাটোত অত্যধিক ছাৰ্ভাৰ ৰিডাইৰেক্ট আছে।" + "ছাৰ্ভাৰৰ প্ৰ\'ট\'কল সমৰ্থিত নহয়৷" + "এটা সুৰক্ষিত সংযোগ স্থাপন কৰিব পৰা নগ\'ল।" + "অমান্য URLৰ বাবে পৃষ্ঠাটো খুলিব পৰা নগ\'ল।" + "ফাইলত খুলিব পৰা নগ\'ল।" + "অনুৰোধ কৰা ফাইলটো বিচাৰি পোৱা নগ\'ল।" + "বহুত বেছি অনুৰোধৰ প্ৰক্ৰিয়া চলি আছে৷ অনুগ্ৰহ কৰি পিছত আকৌ চেষ্টা কৰক৷" + "%1$sত ছাইন ইন কৰাত আসোঁৱাহ" + "ছিংক ত্ৰুটি" + "ছিংক ত্ৰুটি" + "বহুতো %s একেলগে মচা হৈছে।" + "টেবলেটৰ সঞ্চয়াগাৰত খালী ঠাই নাই। ঠাই খালী কৰিবলৈ কিছুমান ফাইল মচক।" + "ঘড়ীৰ সঞ্চয়াগাৰ ভৰি পৰিছে। খালী স্থান উলিয়াবলৈ কিছুমান ফাইল মচক।" + "টিভিৰ সঞ্চয়াগাৰ ভৰি পৰিছে। খালী ঠাই উলিয়াবলৈ কিছুমান ফাইল মচক।" + "ফ\'নৰ সঞ্চয়াগাৰত খালী ঠাই নাই। ঠাই খালী কৰিবলৈ কিছুমান ফাইল মচক।" + + প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ\'ল + প্ৰমাণপত্ৰ প্ৰদানকাৰী কৰ্তৃপক্ষ ইনষ্টল কৰা হ\'ল + + "এটা অজ্ঞাত তৃতীয় পক্ষৰদ্বাৰা" + "আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ প্ৰশাসকে পৰ্যবেক্ষণ কৰি আছে" + "%sৰ দ্বাৰা" + "কৰ্মস্থানৰ প্ৰ\'ফাইল মচা হ\'ল" + "প্ৰশাসক এপ্ নথকাৰ বাবে কৰ্মস্থানৰ প্ৰ\'ফাইল মচা হ\'ল" + "কৰ্মস্থানৰ প্ৰ\'ফাইলৰ প্ৰশাসক এপ্ নাই বা ব্যৱহাৰযোগ্য হৈ থকা নাই। যাৰ ফলত আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল আৰু ইয়াৰ লগত জড়িত অন্য ডেটাসমূহ মচা হৈছে। সহায়ৰ বাবে আপোনাৰ প্ৰশাসকৰ সৈতে সম্পর্ক কৰক।" + "আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল এই ডিভাইচটোত আৰু উপলব্ধ নহয়" + "বহুতবাৰ ভুলকৈ পাছৱৰ্ড দিয়া হৈছে" + "পৰিচালিত ডিভাইচ" + "আপোনাৰ প্ৰতিষ্ঠানটোৱে এই ডিভাইচটো পৰিচালনা কৰে আৰু ই নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ কৰিব পাৰে। সবিশেষ জানিবলৈ টিপক।" + "আপোনাৰ ডিভাইচৰ ডেটা মচা হ\'ব" + + + + + "মই" + "টে\'বলেটৰ বিকল্পসমূহ" + "টিভিৰ বিকল্পসমূহ" + "ফ\'নৰ বিকল্পসমূহ" + "নিঃশব্দ ম\'ড" + "ৱায়াৰলেছ অন কৰক" + "ৱায়াৰলেছ অফ কৰক" + "স্ক্ৰীণ লক" + "পাৱাৰ অফ" + "ৰিংগাৰ অফ আছে" + "ৰিংগাৰ কম্পন অৱস্থাত আছে" + "ৰিংগাৰ অন আছে" + "Android ছিষ্টেমৰ আপডেট" + "আপডেট সাজু কৰি থকা হৈছে…" + "পেকেজ আপডেট কৰা প্ৰক্ৰিয়া চলি আছে…" + "ৰিষ্টাৰ্ট কৰা হৈছে…" + "ফেক্টৰী ডেটা ৰিছেট কৰক" + + + "বন্ধ কৰি থকা হৈছে…" + "আপোনাৰ টে\'বলেটটো বন্ধ হ\'ব।" + "আপোনাৰ টিভি বন্ধ কৰা হ\'ব৷" + "আপোনাৰ ঘড়ী বন্ধ কৰা হ\'ব।" + "আপোনাৰ ফ\'নটো বন্ধ হ\'ব।" + "আপুনি ফ\'নটোৰ পাৱাৰ অফ কৰিব বিচাৰেনে?" + "সুৰক্ষিত ম\'ডলৈ ৰিবুট কৰক" + "আপুনি সুৰক্ষিত ম\'ডলৈ ৰিবুট কৰিব বিচাৰেনে? এই কার্যই আপুনি ইনষ্টল কৰা তৃতীয় পক্ষৰ সকলো এপ্লিকেশ্বন অক্ষম কৰিব। আপুনি পুনৰ ৰিবুট কৰিলে সেইবোৰ পুনঃস্থাপন কৰা হ\'ব।" + "শেহতীয়া" + "কোনো শেহতীয়া এপ্ নাই।" + "টে\'বলেটৰ বিকল্পসমূহ" + "টিভিৰ বিকল্পসমূহ" + "ফ\'নৰ বিকল্পসমূহ" + "স্ক্ৰীণ ল\'ক" + "পাৱাৰ অফ" + "জৰুৰীকালীন কল" + "বাগ সম্পর্কীয় অভিযোগ" + "ছেশ্বন সমাপ্ত কৰক" + + + "বাগ সম্পর্কীয় অভিযোগ লওক" + "এই কার্যই ইমেইল বাৰ্তা হিচাপে পঠিয়াবলৈ আপোনাৰ ডিভাইচৰ বৰ্তমান অৱস্থাৰ বিষয়ে তথ্য সংগ্ৰহ কৰিব৷ ইয়াক বাগ সম্পর্কীয় অভিযোগ পঠিওৱা কাৰ্য আৰম্ভ কৰোঁতে অলপ সময় লাগিব; অনুগ্ৰহ কৰি ধৈৰ্য ধৰক।" + "ইণ্টাৰেক্টিভ অভিযোগ" + "বেছিভাগ পৰিস্থিতিত এয়া ব্যৱহাৰ কৰক। ই আপোনাক অভিযোগৰ অগ্ৰগতি ট্ৰেক কৰিবলৈ, সমস্যাটোৰ সম্পর্কে অধিক বিৱৰণ দিবলৈ আৰু স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি দিয়ে। ই কম ব্যৱহাৰ হোৱা সেই শাখাসমূহক অন্তৰ্ভুক্ত নকৰিব পাৰে যিবোৰক অভিযোগ কৰিবলৈ দীৰ্ঘ সময়ৰ প্ৰয়োজন হয়।" + "সম্পূৰ্ণ অভিযোগ" + "যেতিয়া আপোনাৰ ডিভাইচটোৱে সঁহাৰি নিদিয়া হয় বা ই অতি লেহেমীয়া হৈ পৰে বা যেতিয়া আপোনাক সকলো অভিযোগৰ শাখাৰ প্ৰয়োজন হয় তেতিয়া ছিষ্টেমত কম হস্তক্ষেপৰ বাবে এই বিকল্প ব্যৱহাৰ কৰক। আপোনাক অধিক বিৱৰণ দিবলৈ বা অতিৰিক্ত স্ক্ৰীণশ্বট ল’বলৈ নিদিয়ে।" + + ত্ৰুটি সম্পর্কীয় অভিযোগৰ বাবে %dছেকেণ্ডৰ ভিতৰত স্ক্ৰীণশ্বট লোৱা হ\'ব। + ত্ৰুটি সম্পর্কীয় অভিযোগৰ বাবে %dছেকেণ্ডৰ ভিতৰত স্ক্ৰীণশ্বট লোৱা হ\'ব। + + "নিঃশব্দ ম\'ড" + "ধ্বনি অফ আছে" + "ধ্বনি অন আছে" + "এয়াৰপ্লেইন ম\'ড" + "এয়াৰপ্লেইন ম\'ড অন কৰা আছে" + "এয়াৰপ্লেইন ম\'ড অফ কৰা আছে" + + + + + + + "ছেটিংসমূহ" + + + + + + + "৯৯৯+" + "নতুন জাননী" + "ভাৰ্শ্বুৱল কীব\'ৰ্ড" + "কায়িক কীব’ৰ্ড" + "সুৰক্ষা" + "গাড়ী ম\'ড" + "একাউণ্টৰ স্থিতি" + "বিকাশকৰ্তাৰ বাৰ্তাসমূহ" + "আপডেটবোৰ" + "নেটৱৰ্কৰ স্থিতি" + "নেটৱৰ্ক সম্পৰ্কীয় সতৰ্কবাণী" + "নেটৱৰ্ক উপলব্ধ" + "ভিপিএনৰ স্থিতি" + "ডিভাইচৰ প্ৰশাসন" + "সতৰ্কবাণীসমূহ" + "খুচুৰা ডেম\'" + "ইউএছবি সংযোগ" + "এপ্ চলি আছে" + "বেটাৰি খৰচ কৰা এপসমূহ" + "%1$sএ বেটাৰি ব্যৱহাৰ কৰি আছে" + "%1$dটা এপে বেটাৰি ব্যৱহাৰ কৰি আছে" + "বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক" + "%1$s, %2$s" + "সুৰক্ষিত ম\'ড" + "Android ছিষ্টেম" + + + + + + + + + "<b>%1$s</b>ক আপোনাৰ সম্পৰ্কসূচী চাবলৈ অনুমতি দিয়ক" + + + "এই ডিভাইচৰ অৱস্থান ব্যৱহাৰ কৰিব পাৰে" + "<b>%1$s</b>ক ডিভাইচৰ অৱস্থান জানিবলৈ অনুমতি দিয়ক" + "কেলেণ্ডাৰ" + + + "<b>%1$s</b>ক আপোনাৰ কেলেণ্ডাৰ চাবলৈ অনুমতি দিয়ক" + + + "এছএমএছ বার্তা পঠিয়াব আৰু চাব পাৰে" + "<b>%1$s</b>ক এছএমএছ বাৰ্তা পঠিয়াবলৈ আৰু চাবলৈ অনুমতি দিয়ক" + "সঞ্চয়াগাৰ" + "আপোনাৰ ডিভাইচৰ ফট\', মিডিয়া আৰু ফাইলসমূহ ব্যৱহাৰ কৰিব পাৰে" + "<b>%1$s</b>ক আপোনাৰ ডিভাইচত থকা ফট\', মিডিয়া আৰু ফাইল চাবলৈ অনুমতি দিয়ক" + "মাইক্ৰ\'ফ\'ন" + + + "<b>%1$s</b>ক অডিঅ\' ৰেকৰ্ড কৰিবলৈ অনুমতি দিয়ক" + "কেমেৰা" + + + "<b>%1$s</b>ক ছবি তুলিবলৈ আৰু ভিডিঅ\' ৰেকৰ্ড কৰিবলৈ অনুমতি দিয়ক" + + + + + "<b>%1$s</b>ক ফ\'ন কল কৰিবলৈ আৰু পৰিচালনা কৰিবলৈ অনুমতি দিয়ক" + "শৰীৰৰ ছেন্সৰসমূহ" + "আপোনাৰ দেহৰ গুৰুত্বপূৰ্ণ অংগসমূহৰ অৱস্থাৰ বিষয়ে ছেন্সৰৰ ডেটা লাভ কৰিব পাৰে" + "<b>%1$s</b>ক আপোনাৰ দেহৰ গুৰুত্বপূৰ্ণ অংগসমূহৰ অৱস্থাৰ বিষয়ে ছেন্সৰৰ ডেটা লাভ কৰিবলৈ অনুমতি দিয়ক" + "ৱিণ্ড\' সমল বিচাৰি উলিয়াওক" + "আপুনি যোগাযোগ কৰি থকা ৱিণ্ড\'খনৰ সমল পৰীক্ষা কৰক।" + "স্পৰ্শৰদ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰক" + "টেপ কৰা বস্তুসমূহ ডাঙৰকৈ কোৱা হ\'ব আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ দি স্ক্ৰীণ অন্বেষণ কৰিব পাৰিব।" + "আপুনি লিখা পাঠ নিৰীক্ষণ কৰক" + "ক্ৰেডিট কাৰ্ডৰ নম্বৰ আৰু পাছৱৰ্ডৰ দৰে ব্যক্তিগত ডেটা অন্তৰ্ভুক্ত হ\'ব পাৰে।" + "ডিছপ্লেৰ বিবৰ্ধন নিয়ন্ত্ৰণ কৰক" + "ডিছপ্লেৰ জুমৰ স্তৰ আৰু অৱস্থান নিয়ন্ত্ৰণ কৰক।" + "আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ কৰা কার্যসমূহ কৰক" + "টেপ কৰা, ছোৱাইপ কৰা, পিঞ্চ কৰা আৰু আঙুলিৰ স্পৰ্শেৰে নিৰ্দেশ কৰা অন্যান্য কাৰ্যসমূহ কৰিব পাৰে।" + "ফিংগাৰপ্ৰিণ্ট নিৰ্দেশসমূহ" + + + "স্থিতি দণ্ড অক্ষম কৰক বা সলনি কৰক" + "স্থিতি দণ্ড অক্ষম কৰিবলৈ বা ছিষ্টেম আইকন আঁতৰাবলৈ এপটোক অনুমতি দিয়ে।" + "স্থিতি দণ্ড হ\'ব পাৰে" + "নিজকে স্থিতি দণ্ডৰূপে দেখুওৱাবলৈ এপটোক অনুমতি দিয়ে।" + "স্থিতি দণ্ড সম্প্ৰসাৰিত বা সংকোচিত কৰক" + "স্থিতি দণ্ড বিস্তাৰিত বা সংকুচিত কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "শ্বৰ্টকাট ইনষ্টল কৰিব পাৰে" + "এটা এপ্লিকেশ্বনক ব্যৱহাৰকাৰীৰ হস্তক্ষেপৰ অবিহনে গৃহ স্ক্ৰীণ শ্বৰ্টকাট যোগ কৰিবলৈ অনুমতি দিয়ে।" + "শ্বৰ্টকাট আনইনষ্টল কৰিব পাৰে" + "ব্যৱহাৰকাৰীৰ হস্তক্ষেপৰ অবিহনে গৃহ স্ক্ৰীণৰ শ্বৰ্টকাটসমূহ আঁতৰাবলৈ এপ্লিকেশ্বনক অনুমতি দিয়ে।" + "বহিৰ্গামী কলসমূহ অন্য ক\'ৰবালৈ পঠিয়াওক" + "এটা বৰ্হিগামী কল কৰি থকাৰ সময়ত ডায়েল কৰা নম্বৰ চাবলৈ আৰু লগতে এটা পৃথক নম্বৰলৈ কল সংযোগ কৰিবলৈ বা সকলোকে একেলগে বন্ধ কৰিবলৈ এপক অনুমতি দিয়ে।" + "ফ\'ন কলৰ উত্তৰ দিব পাৰে" + "এপটোক অন্তৰ্গামী ফ\'ন কলৰ উত্তৰ দিবলৈ অনুমতি দিয়ে।" + "পাঠ বার্তা (এছএমএছ) বোৰ লাভ কৰক" + "এপটোক এছএমএছ বাৰ্তাবোৰ পাবলৈ আৰু প্ৰক্ৰিয়া সম্পন্ন কৰিবলৈ অনুমতি দিয়ে৷ ইয়াৰ অৰ্থ এইটোৱেই যে এপটোৱে আপোনাক বাৰ্তাবোৰ নেদেখুৱাকৈয়ে আপোনাৰ ডিভাইচলৈ পঠিওৱা বাৰ্তাবোৰ নিৰীক্ষণ কৰিব বা মচিব পাৰে৷" + "পাঠ বার্তা (এমএমএছ) বোৰ লাভ কৰক" + "এমএমএছ বার্তাবোৰ লাভ আৰু ইয়াৰ প্ৰক্ৰিয়া সম্পন্ন কৰিবলৈ এপক অনুমতি দিয়ে। ইয়াৰ অৰ্থ হৈছে এই এপে আপোনাৰ ডিভাইচলৈ প্ৰেৰণ কৰা বার্তাসমূহ আপোনাক নেদেখুৱাকৈয়ে পৰ্যবেক্ষণ আৰু মচিব পাৰে।" + "চেল সম্প্ৰচাৰৰ বার্তাবোৰ পঢ়ক" + "আপোনাৰ ডিভাইচে লাভ কৰা চেল সম্প্ৰচাৰৰ বার্তাবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। আপোনাক জৰুৰীকালীন পৰিস্থিতিবোৰত সর্তক কৰিবলৈ চেল সম্প্ৰচাৰৰ বার্তাবোৰ প্ৰেৰণ কৰা হয়। জৰুৰীকালীন চেল সম্প্ৰচাৰ লাভ কৰাৰ সময়ত আপোনাৰ ডিভাইচৰ কাৰ্যদক্ষতা বা কাৰ্যপ্ৰণালীত ক্ষতিকাৰক এপবোৰে হস্তক্ষেপ কৰিব পাৰে।" + "আপুনি সদস্যভুক্ত হোৱা ফীডসমূহ পঢ়ক" + "বৰ্তমান ছিংক কৰা ফীডৰ সবিশেষ লাভ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + + + "এপটোক এছএমএছ বাৰ্তা পঠিয়াবলৈ অনুমতি দিয়ে৷ ইয়াৰ ফলত অপ্ৰত্যাশিত মাচুল ভৰিবলগা হ\'ব পাৰে৷ ক্ষতিকাৰক এপসমূহে আপোনাৰ অনুমতি নোলোৱাকৈয়ে বাৰ্তা পঠিয়াই আপোনাৰ পৰা মাচুল কাটিব পাৰে৷" + "আপোনাৰ পাঠ বার্তাবোৰ পঢ়ক (এছএমএছ বা এমএমএছ)" + "এই এপটোৱে আপোনাৰ টেবলেটটোত সংৰক্ষিত সকলো এছএমএছ (পাঠ) বাৰ্তা পঢ়িব পাৰে।" + "এই এপটোৱে আপোনাৰ টিভিটোত সংৰক্ষিত সকলো এছএমএছ (পাঠ) বাৰ্তা পঢ়িব পাৰে।" + "এই এপটোৱে আপোনাৰ ফ\'নত সংৰক্ষিত সকলো এছএমএছ (পাঠ) বাৰ্তা পঢ়িব পাৰে।" + "পাঠ বার্তা (WAP) বোৰ লাভ কৰক" + "এপটোক WAP বাৰ্তাবোৰ পাবলৈ আৰু প্ৰক্ৰিয়া সম্পন্ন কৰিবলৈ অনুমতি দিয়ে৷ এই অনুমতিত আপোনালৈ পঠিওৱা বাৰ্তাবোৰ আপোনাক নেদেখুৱাকৈয়ে নিৰীক্ষণ বা মচাৰ সক্ষমতা অন্তৰ্ভুক্ত থাকে৷" + "চলি থকা এপসমূহ বিচাৰি উলিয়াওক" + "এপটোক বৰ্তমানে আৰু শেহতীয়াভাৱে চলি থকা কাৰ্যসমূহৰ বিষয়ে তথ্য পুনৰুদ্ধাৰ কৰিবলৈ অনুমতি দিয়ে৷ এইটোৱে এপটোক ডিভাইচটোত কোনবোৰ এপ্লিকেশ্বন ব্যৱহাৰ হৈ আছে তাৰ বিষয়ে তথ্য বিচাৰি উলিয়াবলৈ অনুমতি দিব পাৰে৷" + "প্ৰ\'ফাইল আৰু ডিভাইচৰ গৰাকীসকলক পৰিচালনা কৰিব পাৰে" + + + "চলি থকা এপসমূহক পুনৰাই ক্ৰমবদ্ধ কৰক" + "গতিবিধিক অগ্ৰভাগ আৰু নেপথ্যলৈ নিবলৈ এপক অনুমতি দিয়ে। এপে এই কার্য আপোনাৰ ইনপুট অবিহনেই কৰিব পাৰে।" + "গাড়ীৰ ম\'ড সক্ষম কৰক" + "গাড়ী ম\'ড সক্ষম কৰিবলৈ এপটোক অনুমতি দিয়ে৷" + "অন্য এপবোৰ বন্ধ কৰক" + "এপটোক অন্য এপসমূহৰ নেপথ্যৰ প্ৰক্ৰিয়াসমূহ শেষ কৰিবলৈ অনুমতি দিয়ে৷ এই কার্যৰ বাবে অন্য এপসমূহ চলাটো বন্ধ হ\'ব পাৰে৷" + "এই এপটো অইন এপৰ ওপৰত প্ৰদৰ্শিত হ\'ব পাৰে" + "এই এপটো অইন এপৰ ওপৰত বা স্ক্ৰীণৰ অইন অংশত প্ৰদৰ্শিত হ\'ব পাৰে। এই কাৰ্যই এপসমূহৰ স্বাভাৱিক কাৰ্যকলাপত ব্যাঘাত জন্মাব পাৰে আৰু অইন এপসমূহক স্ক্ৰীণত কেনেকৈ দেখা পোৱা যায় সেইটো সলনি কৰিব পাৰে।" + "নেপথ্যত চলিব পাৰে" + "এই এপটো নেপথ্যত চলিব পাৰে। ইয়াৰ ফলত বেটাৰি সোনকালে শেষ হ\'ব পাৰে।" + "নেপথ্যত ডেটা ব্যৱহাৰ কৰিব পাৰে" + "এই এপটোৱে নেপথ্যত ডেটা ব্যৱহাৰ কৰিব পাৰে। ইয়াৰ ফলত ডেটা বেছি খৰছ হ\'ব পাৰে।" + "এপক সদায়ে চলি থকা কৰক" + "মেম\'ৰিত নিজৰ বাবে প্ৰয়োজনীয় ঠাই পৃথক কৰিবলৈ এপক অনুমতি দিয়ে। এই কার্যই টেবলেটৰ কার্যক লেহেমীয়া কৰি অন্য এপবোৰৰ বাবে উপলব্ধ মেম\'ৰিক সীমাবদ্ধ কৰে।" + "মেম\'ৰিত নিজৰ বাবে প্ৰয়োজনীয় ঠাই পৃথক কৰিবলৈ এপটোক অনুমতি দিয়ে। এই কার্যই অন্য এপবোৰৰ বাবে উপলব্ধ মেম\'ৰিক সীমাবদ্ধ কৰে যাৰ বাবে টিভিটো লেহেমীয়া হয়।" + "মেম\'ৰিত নিজৰ বাবে প্ৰয়োজনীয় ঠাই পৃথক কৰিবলৈ এপক অনুমতি দিয়ে। এই কার্যই ফ\'নৰ কার্যক লেহেমীয়া কৰি অন্য এপবোৰৰ বাবে উপলব্ধ মেম\'ৰিক সীমাবদ্ধ কৰে।" + + + + + "এপৰ সঞ্চয়াগাৰৰ খালী ঠাই হিচাপ কৰক" + "এপটোক ইয়াৰ ক\'ড, ডেটা আৰু কেশ্বৰ আকাৰ বিচাৰি উলিয়াবলৈ অনুমতি দিয়ে" + "ছিষ্টেম ছেটিংসমূহ সংশোধন কৰক" + "এপটোক ছিষ্টেমৰ ছেটিংসমূহৰ ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ ক্ষতিকাৰক এপসমূহে আপোনাৰ ছিষ্টেম কনফিগাৰেশ্বনক ক্ষতিগ্ৰস্ত কৰিব পাৰে৷" + "আৰম্ভ হোৱাৰ সময়ত চলাওক" + "ছিষ্টেমে বুট কৰা কাৰ্য সমাপ্ত কৰাৰ লগে লগে এপটোক নিজে নিজে আৰম্ভ হ\'বলৈ অনুমতি দিয়ে। ইয়াৰ ফলত ফ\'নটো ষ্টাৰ্ট হওতে বেছি সময়ৰ প্ৰয়োজন হ\'ব পাৰে, আৰু এপটো সদায় চলি থকাৰ কাৰণে ফ\'নটো সামগ্ৰিকভাৱে লেহেমীয়া হ\'ব পাৰে।" + "ছিষ্টেমে বুটিং সমাপ্ত কৰাৰ লগে লগে এপটোক নিজে নিজে আৰম্ভ হ\'বলৈ অনুমতি দিয়ে। এই কাৰ্যটোৰ বাবে টিভিটো আৰম্ভ হ\'বলৈ দীৰ্ঘ সময়ৰ প্ৰয়োজন হ\'ব পাৰে আৰু সদায়ে ই চলি থকাৰ বাবে টে\'বলেটটো সামগ্ৰিকভাৱে লেহেমীয়া হৈ পৰিব পাৰে।" + "ছিষ্টেমে বুট কৰা কাৰ্য সমাপ্ত কৰাৰ লগে লগে এপটোক নিজে নিজে আৰম্ভ হ\'বলৈ অনুমতি দিয়ে। ইয়াৰ ফলত ফ\'নটো ষ্টাৰ্ট হওতে বেছি সময়ৰ প্ৰয়োজন হ\'ব পাৰে, আৰু এপটো সদায় চলি থকাৰ কাৰণে ফ\'নটো সামগ্ৰিকভাৱে লেহেমীয়া হ\'ব পাৰে।" + "ষ্টিকী ব্ৰ\'ডকাষ্ট পঠিয়াওক" + "সম্প্ৰচাৰৰ শেষত বাকী ৰোৱা ষ্টিকী ব্ৰ\'ডকাষ্টবোৰ প্ৰেৰণ কৰিবলৈ এপক অনুমতি দিয়ে। ইয়াক অত্য়ধিক ব্যৱহাৰ কৰাৰ ফলত মেম\'ৰি অধিক খৰচ হোৱাৰ বাবে টেবলেট লেহেমীয়া বা অস্থিৰ হৈ পৰে।" + "এপটোক ষ্টিকী ব্ৰ\'ডকাষ্ট প্ৰেৰণ কৰিবলৈ এপক অনুমতি দিয়ে, যিবোৰ সম্প্ৰচাৰ শেষ হোৱাৰ পিছতো ৰৈ যায়। ইয়াক অত্য়ধিক ব্যৱহাৰ কৰিলে মেম\'ৰি অধিক খৰচ হোৱাৰ বাবে টিভিটো লেহেমীয়া বা অস্থিৰ হৈ পৰিব পাৰে।" + "সম্প্ৰচাৰৰ শেষত বাকী ৰোৱা ষ্টিকী ব্ৰ\'ডকাষ্টবোৰ প্ৰেৰণ কৰিবলৈ এপক অনুমতি দিয়ে। ইয়াক অত্য়ধিক ব্যৱহাৰ কৰাৰ ফলত মেম\'ৰি অধিক খৰচ হোৱাৰ বাবে ফ\'নটো লেহেমীয়া বা অস্থিৰ হৈ পৰে।" + "নিজৰ সম্পর্ক সূচী পঢ়ক" + "আপুনি কোনো ব্যক্তি বিশেষৰ সৈতে টেবলেট, ইমেইল বা অন্য মাধ্যমেৰে কিমান সঘনাই যোগাযোগ কৰিছে সেই তথ্য়সহ ফ\'নৰ সম্পর্কসূচীত সঞ্চয় কৰা ডেটা পঢ়িবলৈ এপক অনুমতি দিয়ে৷ এই কার্যই এপক আপোনাৰ সম্পর্কৰ ডেটা ছেভ কৰিবলৈ অনুমতি দিয়ে আৰু ক্ষতিকাৰক এপবোৰে সম্পর্কসূচীৰ ডেটা আপোনাৰ অজ্ঞাতেই শ্বেয়াৰ কৰিব পাৰে৷" + "এপটোক আপোনাৰ টিভিত জমা থকা সম্পৰ্কসমূৰ বিষয়ে থকা ডেটা পঢ়িবলৈ অনুমতি দিয়াৰ লগতে আপুনি কোনো বিশেষ ব্যক্তিৰ সৈতে কিমান সঘনাই কল, ইমেইল বা অন্য মাধ্যমেৰে যোগাযোগ কৰিছে তাকো জানিবলৈ সুযোগ দিয়ে। এই বিশেষ অনুমতিটোৰ বাবে এপটোৱে আপোনাৰ সম্পর্ক ডেটা ছেভ কৰিব পাৰে, আৰু ক্ষতিকাৰক এপবোৰে সম্পর্কসূচীৰ ডেটা আপুনি নজনাকৈ শ্বেয়াৰ কৰিব পাৰে৷" + "আপুনি কোনো বিশেষ ব্যক্তিৰ সৈতে কিমান সঘনাই ফ\'ন, ইমেইল বা অন্য মাধ্যমেৰে যোগাযোগ কৰে সেই সম্পর্কে ফ\'নৰ সম্পর্ক সূচীত সঞ্চয় কৰা ডেটা পঢ়িবলৈ এপক অনুমতি দিয়ে। এই অনুমতিএ এপক আপোনাৰ সম্পর্কৰ ডেটা ছেভ কৰিবলৈ দিয়ে আৰু ক্ষতিকাৰক এপবোৰে সম্পর্কৰ ডেটা আপোনাৰ অজ্ঞাতেই শ্বেয়াৰ কৰিব পাৰে।" + "আপোনাৰ সম্পর্ক সূচী সংশোধন কৰক" + "আপুনি ব্য়ক্তি বিশেষক কিমান সঘনাই কল কৰিছে, ইমেইল কৰিছে বা অন্য উপায়েৰে যোগাযোগ কৰিছে তাক অন্তৰ্ভুক্ত কৰি এপটোক আপোনাৰ টেবলেটত সঞ্চয় কৰি ৰখা সম্পৰ্ক সূচীৰ ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ এই অনুমতি দিলে এপসমূহে সম্পৰ্কসূচীৰ ডেটা মচিব পাৰে।" + "আপুনি যিসকল লোকক কল কৰিছে, ইমেইল কৰিছে বা সম্পৰ্কসূচীৰ বিশেষ লোকসকলৰ লগত অন্য উপায়েৰে যোগাযোগ কৰিছে তাক অন্তৰ্ভুক্ত কৰি এপটোক আপোনাৰ টিভিত সঞ্চয় কৰি ৰখা ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে। এই অনুমতিয়ে এপসমূহক সম্পৰ্কসূচীৰ ডেটা মচি পেলাবলৈ অনুমতি দিয়ে৷" + "আপুনি কোনো বিশেষ ব্যক্তিৰ সৈতে কিমান সঘনাই ফ\'ন, ইমেইল বা অন্য মাধ্যমেৰে যোগাযোগ কৰে সেই সম্পর্কে ফ\'নৰ সম্পর্ক সূচীত সঞ্চয় কৰা ডেটা পঢ়িবলৈ এপক অনুমতি দিয়ে। এই কার্যই এপক সম্পর্কৰ ডেটা মচিবলৈ অনুমতি দিয়ে।" + "কল লগ পঢ়ক" + "এই এপে আপোনাৰ কলৰ ইতিহাস পঢ়িব পাৰে।" + "কল লগ লিখক" + "অন্তৰ্গামী আৰু বহিৰ্গামী কলৰ ডেটাকে ধৰি আপোনাৰ টেবলেটৰ কল লগ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। ক্ষতিকাৰক এপবোৰে আপোনাৰ কল লগ মচিবলৈ বা সংশোধন কৰিবলৈ ইয়াক ব্যৱহাৰ কৰিব পাৰে।" + "অন্তৰ্গামী আৰু বহিৰ্গামী কলৰ ডেটাকে ধৰি আপোনাৰ টিভিৰ কল লগ সংশোধন কৰিবলৈ এপটোক অনুমতি দিয়ক। ক্ষতিকাৰক এপ্বোৰে আপোনাৰ কল লগ মচিবলৈ বা সংশোধন কৰিবলৈ এয়া ব্যৱহাৰ কৰিব পাৰে।" + "অন্তৰ্গামী আৰু বহিৰ্গামী কলৰ ডেটাকে ধৰি আপোনাৰ ফ\'নৰ কল লগ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। ক্ষতিকাৰক এপবোৰে আপোনাৰ কল লগ মচিবলৈ বা সংশোধন কৰিবলৈ ইয়াক ব্যৱহাৰ কৰিব পাৰে।" + "শৰীৰৰ ছেন্সৰসমূহ (যেনে হৃদপিণ্ডৰ গতিৰ হাৰ নিৰীক্ষক) ব্যৱহাৰ কৰিব পাৰে" + "আপোনাৰ হৃদস্পন্দনৰ দৰে শাৰীৰিক অৱস্থাক নিৰীক্ষণ কৰা ছেন্সৰৰ পৰা ডেটা লাভ কৰিবলৈ এপক অনুমতি দিয়ে।" + "কেলেণ্ডাৰৰ কাৰ্যক্ৰম আৰু সবিশেষ পঢ়িব পাৰে" + "এই এপটোৱে আপোনাৰ টেবলেটটোত সংৰক্ষিত সকলো কেলেণ্ডাৰ কাৰ্যক্ৰম পঢ়িব পাৰে আৰু আপোনাৰ কেলেণ্ডাৰৰ ডেটা শ্বেয়াৰ বা ছেভ কৰিব পাৰে।" + "এই এপটোৱে আপোনাৰ টিভিটোত সংৰক্ষিত সকলো কেলেণ্ডাৰৰ কাৰ্যক্ৰম পঢ়িব পাৰে আৰু আপোনাৰ কেলেণ্ডাৰৰ ডেটা শ্বেয়াৰ বা ছেভ কৰিব পাৰে।" + "এই এপটোৱে আপোনাৰ ফ\'নটোত সংৰক্ষিত সকলো কেলেণ্ডাৰ কাৰ্যক্ৰম পঢ়িব পাৰে আৰু আপোনাৰ কেলেণ্ডাৰৰ ডেটা শ্বেয়াৰ বা ছেভ কৰিব পাৰে।" + "গৰাকীয়ে নজনাকৈয়ে কেলেণ্ডাৰৰ কাৰ্যক্ৰম সংশোধন কৰি অতিথিসকললৈ ইমেইল প্ৰেৰণ কৰক" + "এই এপটোৱে আপোনাৰ টেবলেটত কেলেণ্ডাৰ কাৰ্যক্ৰম যোগ কৰিব, আঁতৰাব বা সলনি কৰিব পাৰে। ই এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগিব বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷" + "এই এপটোৱে আপোনাৰ টিভিত কেলেণ্ডাৰ কাৰ্যক্ৰম যোগ কৰিব, আঁতৰাব বা সলনি কৰিব পাৰে। এই এপে এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগিব বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷" + "এই এপে আপোনাৰ ফ\'নৰ কেলেণ্ডাৰত কার্যক্ৰম যোগ দিব, আঁতৰাব বা সলনি কৰিব পাৰে। ই এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগে বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷" + "অতিৰিক্ত অৱস্থান দেখুওৱা নির্দেশত প্ৰৱেশ কৰক" + "অৱস্থানৰ অতিৰিক্ত নির্দেশনাসমূহত প্ৰৱেশ কৰিবলৈ এপক অনুমতি দিয়ে। ইয়ে এপটোক জিপিএছ বা অন্য অৱস্থান উৎসসমূহৰ কাৰ্যকলাপত হস্তক্ষেপ কৰাৰ সুযোগ দিব পাৰে।" + "সঠিক অৱস্থান (জিপিএছ আৰু নেটৱর্ক ভিত্তিক) ব্যৱহাৰ কৰিব পাৰে" + "এই এপটোৱে জিপিএছ বা নেটৱর্কৰ উৎসসমূহ যেনে চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্ক আদিক ভিত্তি কৰি আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ ফ\'নটোত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব। এই কার্যই অধিক বেটাৰি খৰচ কৰিব পাৰে।" + "অনুমানিক অৱস্থান (নেটৱর্ক ভিত্তিক) ব্যৱহাৰ কৰিব পাৰে" + "চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টেবলেটটোত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।" + "চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টিভিত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।" + "এই এপটোৱে ম\'বাইলৰ টাৱাৰ আৰু ৱাই-ফাইৰ নেটৱৰ্ক আদিৰ দৰে নেটৱৰ্কৰ উৎসসমূহক ভিত্তি কৰি আপোনাৰ অৱস্থান চিনাক্ত কৰিব পাৰে। সেই অৱস্থান সেৱাসমূহ আপোনাৰ ফ\'নত সক্ষম অৱস্থাত থাকিলেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিব পাৰিব।" + "আপোনাৰ অডিঅ\' ছেটিংসমূহ সলনি কৰক" + "এপটোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিংসমূহ যাৰ স্পীকাৰক আউটপুটৰ বাবে ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷" + "অডিঅ\' ৰেকর্ড কৰক" + "এই এপটোৱে যিকোনো সময়তে মাইক্ৰ\'ফ\'ন ব্যৱহাৰ কৰি অডিঅ\' ৰেকৰ্ড কৰিব পাৰে।" + "ছিমলৈ নিৰ্দেশ পঠিয়াব পাৰে" + "ছিমলৈ নিৰ্দেশসমূহ প্ৰেৰণ কৰিবলৈ এপক অনুমতি দিয়ে। ই অতি ক্ষতিকাৰক।" + "ফট\' তোলা আৰু ভিডিঅ\' ৰেকৰ্ড কৰা" + "এই এপে যিকোনো সময়তে কেমেৰা ব্যৱহাৰ কৰি ফট\' তুলিব আৰু ভিডিঅ\' ৰেকর্ড কৰিব পাৰে।" + "কম্পন নিয়ন্ত্ৰণ কৰক" + "ভাইব্ৰেটৰ নিয়ন্ত্ৰণ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "পোনপটীয়াকৈ ফ\'ন নম্বৰলৈ কল কৰক" + "আপোনাৰ কোনো ব্যাঘাত নোহোৱাকৈ ফ\'ন নম্বৰবোৰত কল কৰিবলৈ এপক অনুমতি দিয়ে৷ ইয়াৰ ফলত অপ্ৰত্যাশিত মাচুল ভৰিবলগা বা কলবোৰ কৰা হ\'ব পাৰে৷ মনত ৰাখিব যে ই এপটোক জৰুৰীকালীন নম্বৰবোৰত কল কৰিবলৈ অনুমতি নিদিয়ে৷ ক্ষতিকাৰক এপসমূহে আপোনাৰ অনুমতি নোলোৱাকৈয়ে কল কৰি আপোনাক টকা খৰছ কৰাব পাৰে৷" + "আইএমএছ কল সেৱা ব্যৱহাৰ কৰিব পাৰে" + "আপোনাৰ হস্তক্ষেপৰ অবিহনে আইএমএছ সেৱা ব্যৱহাৰ কৰি কল কৰিবলৈ এপক অনুমতি দিয়ে।" + "ফ\'নৰ স্থিতি আৰু পৰিচয় পঢ়ক" + "ডিভাইচত থকা ফ\'নৰ সুবিধাসমূহ ব্য়ৱহাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷ এই অনুমতিয়ে কোনো কল সক্ৰিয় হৈ থাককেই বা নাথাকক আৰু দূৰবৰ্তী নম্বৰটো কলৰ দ্বাৰা সংযোজিত হওকেই বা নহওক এপটোক ফ\'ন নম্বৰ আৰু ডিভাইচৰ পৰিচয় নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে৷" + "ছিষ্টেমৰ জৰিয়তে কল কৰিব পাৰে" + "কল কৰাৰ অভিজ্ঞতাক উন্নত কৰিবলৈ এপটোক ছিষ্টেমৰ জৰিয়তে কলসমূহ কৰিবলৈ দিয়ে।" + + + + + "ফ\'ন নম্বৰসমূহ পঢ়ে" + "এপটোক ডিভাইচটোৰ ফ\'ন নম্বৰসমূহ চাবলৈ অনুমতি দিয়ে।" + "টে\'বলেট সুপ্ত অৱস্থালৈ যোৱাত বাধা দিয়ক" + "টিভি সুপ্ত অৱস্থালৈ যোৱাত বাধা দিয়ে" + "ফ\'ন সুপ্ত অৱস্থালৈ যোৱাত বাধা দিয়ক" + "টে\'বলেট সুপ্ত অৱস্থালৈ যোৱাৰ পৰা প্ৰতিৰোধ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "টিভিটোক সুপ্ত অৱস্থালৈ যোৱাৰ পৰা প্ৰতিৰোধ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "ফ\'ন সুপ্ত অৱস্থালৈ যোৱাৰ পৰা প্ৰতিৰোধ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "ইনফ্ৰাৰেড ট্ৰান্সমিট কৰিব পাৰে" + "টে\'বলেটৰ ইনফ্ৰাৰেড ট্ৰান্সমিটাৰ ব্যৱহাৰ কৰিবলৈ এপক অনুমতি দিয়ে।" + "টিভিৰ ইনফ্ৰাৰেড ট্ৰান্সমিটাৰ ব্যৱহাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "ফ\'নৰ ইনফ্ৰাৰেড ট্ৰান্সমিটাৰ ব্যৱহাৰ কৰিবলৈ এপক অনুমতি দিয়ে।" + "ৱালপেপাৰ ছেট কৰক" + "ছিষ্টেমৰ ৱালপেপাৰ ছেট কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "আপোনাৰ ৱালপেপাৰৰ আকাৰ মিলাওক" + "ছিষ্টেমৰ ৱালপেপাৰৰ আকাৰ হিণ্ট ছেট কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "সময় মণ্ডল ছেট কৰক" + "টে\'বলেটৰ সময় মণ্ডল সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "টিভিৰ সময় মণ্ডল সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "ফ\'নৰ সময় মণ্ডল সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "ডিভাইচত একাউণ্টবোৰ বিচাৰক" + "এপটোক টেবলেটটোৰ জ্ঞাত একাউণ্টসমূহৰ সূচীখন পাবলৈ অনুমতি দিয়ে৷ এইটোৱে আপুনি ইনষ্টল কৰি ৰখা এপ্লিকেশ্বনসমূহে সৃষ্টি কৰা যিকোনো একাউণ্টক অন্তৰ্ভুক্ত কৰিব পাৰে৷" + "এপটোক টিভিটোৱে জনা একাউণ্টসমূহৰ সূচীখন পাবলৈ অনুমতি দিয়ে। এইটোৱে আপুনি ইনষ্টল কৰি ৰখা এপ্লিকেশ্বনসমূহৰ দ্বাৰা সৃষ্ট যিকোনো একাউণ্টক অন্তৰ্ভুক্ত কৰিব পাৰে৷" + "এপটোক ফ\'নটোৰ জ্ঞাত একাউণ্টসমূহৰ সূচীখন পাবলৈ অনুমতি দিয়ে৷ এইটোৱে আপুনি ইনষ্টল কৰি ৰখা এপ্লিকেশ্বনসমূহে সৃষ্টি কৰা যিকোনো একাউণ্টক অন্তৰ্ভুক্ত কৰিব পাৰে৷" + "নেটৱৰ্কৰ সংযোগবোৰ চাওক" + "মজুত থকা আৰু সংযোগ হৈ থকা নেটৱৰ্ক সংযোগসমূহৰ বিষয়ে তথ্য চাবলৈ এপটোক অনুমতি দিয়ে৷" + "সম্পূর্ণ নেটৱর্কৰ সুবিধা লাভ কৰিব পাৰে" + "এপটোক নেটৱৰ্ক ছ\'কেটবোৰ সৃষ্টি কৰিবলৈ আৰু কাষ্টম নেটৱৰ্ক প্ৰ\'ট\'কল ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়ে৷ ব্ৰাউজাৰ আৰু অন্য এপ্লিকেশ্বনসমূহে ইণ্টাৰনেটলৈ ডেটা পঠিওৱা মাধ্য়ম প্ৰদান কৰে, গতিকে ইণ্টাৰনেটলৈ ডেটা পঠিয়াবলৈ এই অনুমতিৰ প্ৰয়োজন নাই৷" + "নেটৱৰ্কৰ সংযোগ সলনি কৰক" + "নেটৱৰ্ক সংযোগৰ অৱস্থাটো সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "টেডাৰিং সংযোগ সলনি কৰক" + "টেডাৰ হৈ থকা ইণ্টাৰনেট সংযোগৰ অৱস্থা সলনি কৰিবলৈ এপটোক অনুমতি দিয়ে৷" + "ৱাই-ফাইৰ সংযোগবোৰ চাওক" + "ৱাই-ফাই সক্ষম কৰা হ\'ল নে নাই আৰু সংযোগ হৈ থকা ৱাই-ফাই ডিভাইচসমূহৰ নামবোৰৰ দৰে ৱাই-ফাইৰ ইণ্টাৰনেট সম্পর্কীয় তথ্য চাবলৈ এপক অনুমতি দিয়ে।" + "ৱাই-ফাই সংযোগ কৰক আৰু ইয়াৰ সংযোগ বিচ্ছিন্ন কৰক" + "এপটোক ৱাই-ফাই এক্সেছ পইণ্টলৈ সংযোগ কৰিবলৈ আৰু তাৰ সংযোগ বিচ্ছিন্ন কৰিবলৈ আৰু ৱাই-ফাই নেটৱৰ্কসমূহৰ বাবে ডিভাইচ কনফিগাৰেশ্বনত সাল-সলনি কৰিবলৈ অনুমতি দিয়ে৷" + "ৱাই-ফাই মাল্টিকাষ্ট প্ৰচাৰৰ অনুমতি দিয়ক" + "আপোনাৰ টেবলেটৰ লগতে মাল্টিকাষ্ট ঠিকনাবোৰ ও ব্যৱহাৰ কৰি এপক ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট প্ৰাপ্ত কৰিবলৈ অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ বেটাৰিৰ অধিক চ্চাৰ্জ ব্যৱহাৰ কৰে।" + "কেৱল আপোনাৰ টিভিটোৱেই নহয়, মাল্টিকাষ্ট ঠিকনা ব্যৱহাৰ কৰি এটা ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট লাভ কৰিবলৈ এপটোক অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ অধিক বেটাৰি ব্যৱহাৰ কৰে।" + "আপোনাৰ ফ\'নৰ লগতে মাল্টিকাষ্ট ঠিকনাবোৰ ও ব্যৱহাৰ কৰি এপক ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট প্ৰাপ্ত কৰিবলৈ অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ বেটাৰিৰ অধিক চ্চাৰ্জ ব্যৱহাৰ কৰে।" + "ব্লুটুথ ছেটিংসমূহ ব্যৱহাৰ কৰক" + "স্থানীয় ব্লুটুথ টে\'বলেট কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰা লগাবলৈ আৰু বিচাৰি উলিয়াবলৈ এপটোক অনুমতি দিয়ে।" + "স্থানীয় ব্লুটুথ টিভি কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰাবদ্ধ কৰিবলৈ আৰু বিচাৰি উলিয়াবলৈ এপক অনুমতি দিয়ে।" + "স্থানীয় ব্লুটুথ ফ\'ন কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰা লগাবলৈ আৰু বিচাৰি উলিয়াবলৈ এপটোক অনুমতি দিয়ে।" + "WiMAXৰ লগত সংযোগ কৰক আৰু ইয়াৰ পৰা সংযোগ বিচ্ছিন্ন কৰক" + "WiMAX সক্ষম হৈ আছেনে নাই আৰু সংযোজিত যিকোনো WiMAX নেটৱৰ্কৰ বিষয়ে তথ্য নিৰ্ধাৰণ কৰিবলৈ এপটোক অনুমতি দিয়ে৷" + "WiMAXৰ স্থিতি সলনি কৰক" + "এপটোক টেবলেটলৈ সংযোগ কৰিবলৈ আৰু WiMAX নেটৱৰ্কসমূহৰ পৰা টেবলেটৰ সংযোগ বিচ্ছিন্ন কৰিবলৈ অনুমতি দিয়ে৷" + "এপটোক টিভিৰ লগত সংযোগ কৰিবলৈ আৰু WiMAX নেটৱৰ্কসমূহৰ পৰা টিভিৰ সংযোগ বিচ্ছিন্ন কৰিবলৈ অনুমতি দিয়ে৷" + "এপটোক ফ\'নলৈ সংযোগ কৰিবলৈ আৰু WiMAX নেটৱৰ্কসমূহৰ পৰা ফ\'নৰ সংযোগ বিচ্ছিন্ন কৰিবলৈ অনুমতি দিয়ে৷" + "ব্লুটুথ ডিভাইচবোৰৰ সৈতে যোৰা লগাওক" + "টেবলেটত ব্লুটুথৰ কনফিগাৰেশ্বন চাবলৈ আৰু যোৰা লগোৱা ডিভাইচসমূহৰ জৰিয়তে সংযোগ কৰিবলৈ আৰু সংযোগৰ অনুৰোধ স্বীকাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷" + "টিভিটোত ব্লুটুথৰ কনফিগাৰেশ্বন চাবলৈ আৰু যোৰ পাতি থোৱা ডিভাইচসমূহৰ সৈতে সংযোগ কৰিবলৈ আৰু গ্ৰহণ কৰিবলৈ এপটোক অনুমতি দিয়ে।" + "ফ\'নটোত ব্লুটুথৰ কনফিগাৰেশ্বন চাবলৈ আৰু যোৰা লগোৱা ডিভাইচসমূহৰ জৰিয়তে সংযোগ কৰিবলৈ আৰু সংযোগৰ অনুৰোধ স্বীকাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷" + "নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক" + "এপটোক নিয়েৰ ফিল্ড কমিউনিকেশ্বন (NFC) টেগ, কাৰ্ড আৰু ৰিডাৰসমূহৰ সৈতে যোগাযোগ কৰিবলৈ অনুমতি দিয়ে।" + "আপোনাৰ স্ক্ৰীণ ল\'ক অক্ষম কৰক" + "এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণ স্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰপিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "অত্যধিক প্ৰয়াস। ফিংগাৰপ্ৰিণ্ট ছেন্সৰ অক্ষম কৰা হ\'ল।" + + + + + + + + + + + "ফিংগাৰপ্ৰিণ্ট আইকন" + "ছিংকৰ ছেটিংসমূহ পঢ়ক" + "একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।" + "ছিংকক অন আৰু অফ ট\'গল কৰক" + "এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণ স্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷" + "ছিংকৰ পৰিসংখ্যা পঢ়ক" + "ছিংকৰ কাৰ্যক্ৰমসমূহৰ ইতিহাস আৰু ছিংক কৰা ডেটাৰ পৰিমাণসহ কোনো একাউণ্টৰ ছিংকৰ তথ্য পঢ়িবলৈ এপক অনুমতি দিয়ে।" + "আপোনাৰ ইউএছবি সঞ্চয়াগাৰৰ সমলবোৰ পঢ়ক" + "আপোনাৰ এছডি কাৰ্ডৰ সমলবোৰ পঢ়ক" + "আপোনাৰ ইউএছবি সঞ্চয়াগাৰৰ সমলসমূহ পঢ়িবলৈ এপক অনুমতি দিয়ে।" + "আপোনাৰ এছডি কাৰ্ডৰ সমল পঢ়িবলৈ এপক অনুমতি দিয়ে।" + "আপোনাৰ ইউএছবি সঞ্চয়াগাৰৰ সমলবোৰ সংশোধন কৰক বা মচক" + "আপোনাৰ এছডি কাৰ্ডৰ সমলবোৰ সংশোধন কৰক বা মচক" + "এপটোক ইউএছবি সঞ্চয়াগাৰত লিখাৰ অনুমতি দিয়ে।" + "এপটোক এছডি কাৰ্ডত লিখাৰ অনুমতি দিয়ে।" + "SIP কল কৰা/পোৱা" + "এপটোক SIP কলসমূহ কৰিবলৈ আৰু পাবলৈ অনুমতি দিয়ে।" + "নতুন টেলিকম ছিম সংযোগসমূহ পঞ্জীয়ন কৰা" + "এপটোক নতুন টেলিকম সংযোগ পঞ্জীয়ন কৰিবলৈ অনুমতি দিয়ে।" + "নতুন টেলিকম সংযোগসমূহ পঞ্জীয়ন কৰা" + "এপটোক নতুন টেলিকম সংযোগ পঞ্জীয়ন কৰিবলৈ অনুমতি দিয়ে।" + "টেলিকম সংযোগ পৰিচালনা কৰা" + "এপটোক টেলিকম সংযোগ পৰিচালনা কৰিবলৈ অনুমতি দিয়ে।" + "ইন-কল স্ক্ৰীণৰ সৈতে সংযোগ স্থাপন" + "ব্যৱহাৰকাৰীয়ে কেতিয়া আৰু কেনেদৰে ইন-কল-স্ক্ৰীণ চাব, তাক নিয়ন্ত্ৰণ কৰিবলৈ এপক অনুমতি দিয়ে।" + "টেলিফ\'নী সেৱাসমূহৰ সৈতে সংযোগ স্থাপন" + "কল কৰিবলৈ/লাভ কৰিবলৈ টেলিফ\'নী সেৱাসমূহৰ সৈতে এপক সংযোগ স্থাপনৰ বাবে অনুমতি দিয়ে।" + "ইন-কল ব্যৱহাৰকাৰীৰ অভিজ্ঞতা প্ৰদান কৰা" + "এপটোক ইন-কল ব্যৱহাৰকাৰীৰ অভিজ্ঞতা প্ৰদান কৰিবলৈ অনুমতি দিয়ে।" + "নেটৱর্কৰ পূৰ্বতে হোৱা ব্যৱহাৰৰ বিষয়ে পঢ়ক" + "এপটোক বিশেষ নেটৱৰ্কবিলাকৰ আৰু এপ্‌সমূহৰ নেটৱৰ্ক ব্যৱহাৰৰ ইতিহাস পঢ়িবলৈ অনুমতি দিয়ে।" + "নেটৱর্কৰ নীতি পৰিচালনা কৰক" + "এপটোক নেটৱৰ্ক সংযোগৰ নীতিসমূহ পৰিচালনা কৰিবলৈ আৰু এপ্-বিশেষ নিয়ম সংজ্ঞাবদ্ধ কৰিবলৈ অনুমতি দিয়ে।" + "নেটৱর্ক ব্যৱহাৰৰ হিচাপ সলনি কৰক" + "এপ অনুসুৰি নেটৱর্কৰ ব্যৱহাৰৰ হিচাপ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। এয়া সাধাৰণ এপবোৰৰ ব্যৱহাৰৰ বাবে নহয়।" + "প্ৰৱেশ জাননীসমূহ" + "অন্য এপসমূহৰদ্বাৰা প\'ষ্ট কৰা জাননীসমূহকে ধৰি জাননী বিচাৰি উলিয়াবলৈ, পৰীক্ষা কৰিবলৈ আৰু মচিবলৈ অনুমতি দিয়ে।" + "এটা জাননী শুনা সেৱাৰ লগত সংযুক্ত হ\'ব পাৰে" + "এটা জাননী শ্ৰৱণ সেৱা প্ৰদানকাৰীৰ শীৰ্ষ স্তৰৰ ইণ্টাৰফেইচৰ লগত সংযুক্ত হ\'বলৈ ধাৰকক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "এটা অৱস্থা প্ৰদানকাৰী সেৱাৰ লগত সংযুক্ত হ\'ব পাৰে" + "অৱস্থা প্ৰদানকাৰী সেৱাৰ শীৰ্ষ স্তৰৰ ইণ্টাৰফেচইলৈ সংযুক্ত হ\'বলৈ বাহকক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "এটা ড্ৰিম সেৱাৰ লগত সংযুক্ত হ\'ব পাৰে" + "এটা ড্ৰিম সেৱাৰ শীৰ্ষ স্তৰৰ ইণ্টাৰফেইচলৈ সংযুক্ত হ\'বলৈ ধাৰকক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "বাহকৰদ্বাৰা প্ৰদান কৰা কনফিগাৰেশ্বন এপক কামত লগাব পাৰে" + "বাহকে যোগান ধৰা কনফিগাৰেশ্বন এপ্ ব্যৱহাৰ কৰিবলৈ ধাৰকক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "নেটৱৰ্ক অৱস্থাসমূহৰ ওপৰত নিৰীক্ষণৰ বাবে শুনিব পাৰে" + "এটা এপ্লিকেশ্বনক নেটৱৰ্ক অৱস্থাসমূহত নিৰীক্ষণৰ বাবে শুনিবলৈ অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "ইনপুট ডিভাইচ কেলিব্ৰেশ্বন সলনি কৰিব পাৰে" + "টাচ্চ স্ক্ৰীণৰ কেলিব্ৰেশ্বন পেৰামিটাৰ সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "DRM প্ৰমাণপত্ৰসমূহলৈ প্ৰৱেশ" + "এটা এপ্লিকেশ্বনক DRM প্ৰমাণপত্ৰ গোটাবলৈ আৰু ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে সাধাৰণতে প্ৰয়োজন নহয়।" + "Android বীম স্থানান্তৰণৰ স্থিতি লাভ কৰিব পাৰে" + "বৰ্তমানৰ Android Beam স্থানান্তৰণসমূহৰ বিষয়ে তথ্য পাবলৈ এই এপ্লিকেশ্বনক অনুমতি দিয়ে" + "DRM প্ৰমাণপত্ৰসমূহ আঁতৰোৱা" + "এটা এপ্লিকেশ্বনক DRM প্ৰমাণপত্ৰ আঁতৰাবলৈ অনুমতি দিয়ে। সাধাৰণ এপসমূহৰ বাবে কেতিয়াও প্ৰয়োজন নহয়।" + "বাহকৰ মেছেজিং সেৱাৰ লগত সংযোগ কৰে" + "বাহক মেছেজিং সেৱাৰ উচ্চ স্তৰৰ ইণ্টাৰফেইচত সংযোগ কৰিবলৈ ধাৰকক অনুমতি দিয়ে। এয়া সাধাৰণ এপবোৰৰ বাবে কেতিয়াও প্ৰয়োজন নহয়।" + + + + + + + + + "পাছৱর্ডৰ নিয়ম ছেট কৰক" + + + "স্ক্ৰীণ আনলক কৰা প্ৰয়াসবোৰ পৰ্যবেক্ষণ কৰিব পাৰে" + "স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে টে\'বলেটটো লক কৰক বা টে\'বলেটটোৰ সকলো ডেটা মোহাৰক।" + "স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে টিভিটো লক কৰক বা টিভিটোৰ সকলো ডেটা মোহাৰক।" + "স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ সকলো ডেটা মোহাৰক।" + + + + + + + + + + + "স্ক্ৰীণখন লক কৰক" + "স্ক্ৰীণ কেনেকৈ আৰু কেতিয়া ল\'ক হ\'ব লাগে সেয়া নিয়ন্ত্ৰণ কৰক।" + "সকলো ডেটা মচক" + "সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি টেবলেটৰ ডেটা মচক।" + "সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি টিভিৰ ডেটা মোহাৰক।" + "সতৰ্কবাণী প্ৰেৰণ নকৰাকৈয়ে ফেক্টৰী ডেটা ৰিছেট কৰি ফ\'নৰ ডেটা মচক।" + + + + + + + + + "ডিভাইচৰ বাবে গ্ল\'বেল প্ৰক্সী ছেট কৰক" + + + + + + + "সঞ্চয়াগাৰৰ এনক্ৰিপশ্বন ছেট কৰক" + "সঞ্চয় কৰি ৰখা ডেটাক এনক্ৰিপ্ট কৰাৰ প্ৰয়োজন।" + "কেমেৰাবোৰ অক্ষম কৰক" + "সকলো ডিভাইচৰ কেমেৰাবোৰ ব্যৱহাৰ কৰাত বাধা দিয়ক।" + "স্ক্ৰীণ লকৰ কিছুমান সুবিধা অক্ষম কৰক" + "স্ক্ৰীণ লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।" + + "ঘৰ" + "ম\'বাইল" + "কৰ্মস্থান" + "কর্মস্থানৰ ফেক্সৰ নম্বৰ" + "ঘৰৰ ফেক্স নম্বৰ" + "পেজাৰ" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + + + "ঘৰ" + "কৰ্মস্থান" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + + + "ঘৰ" + "কৰ্মস্থান" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + + + "ঘৰ" + "কৰ্মস্থান" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + + + "কৰ্মস্থান" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + + + "এআইএম" + "Windows Live" + "Yahoo" + "Skype" + "QQ" + "Google Talk" + "ICQ" + "Jabber" + + "নিজৰ উপযোগিতা অনুযায়ী" + "ঘৰৰ ফ\'ন নম্বৰ" + "ম\'বাইল" + "কৰ্মস্থানৰ ফ\'ন নম্বৰ" + "কর্মস্থানৰ ফেক্সৰ নম্বৰ" + "ঘৰৰ ফেক্স নম্বৰ" + "পেজাৰৰ নম্বৰ" + "অন্যান্য" + "কলবেক কৰক" + "গাড়ী" + "কোম্পানীৰ মুখ্য ফ\'ন নম্বৰ" + "আইএছডিএন" + "মুখ্য ফ\'ন নম্বৰ" + "অন্য ফেক্স নম্বৰ" + "ৰেডিঅ’ ফ\'ন নম্বৰ" + "টেলেক্স ফ\'ন নম্বৰ" + "TTY TDD" + "কৰ্মস্থানৰ ম\'বাইল নম্বৰ" + "কৰ্মস্থানৰ পেজাৰৰ নম্বৰ" + "সহায়ক" + "এমএমএছ" + "নিজৰ উপযোগিতা অনুযায়ী" + "জন্মদিন" + "বর্ষপূর্তি" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + "ঘৰ" + "কৰ্মস্থান" + "অন্যান্য" + "ম\'বাইল" + "নিজৰ উপযোগিতা অনুযায়ী" + "ঘৰৰ ঠিকনা" + "কৰ্মস্থানৰ ঠিকনা" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + "ঘৰ" + "কৰ্মস্থান" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + "এআইএম" + "Windows Live" + "Yahoo" + "Skype" + "QQ" + "Hangouts" + "ICQ" + "Jabber" + "NetMeeting" + "কৰ্মস্থান" + "অন্যান্য" + "নিজৰ উপযোগিতা অনুযায়ী" + "নিজৰ উপযোগিতা অনুযায়ী" + "সহায়ক" + "ভাতৃ" + "শিশু" + "সংগী" + "দেউতা" + "বন্ধু" + "মেনেজাৰ" + "মাতৃ" + "অভিভাৱক" + "সংগী" + "মাধ্যমেৰে" + "সম্বন্ধীয়" + "ভনী" + "পতি-পত্নী" + "নিজৰ উপযোগিতা অনুযায়ী" + "ঘৰৰ ঠিকনা" + "কৰ্মস্থান" + "অন্যান্য" + "এই সম্পৰ্ক চাবলৈ কোনো এপ্লিকেশ্বন পোৱা ন\'গল।" + "পিন ক\'ড লিখক" + "PUK আৰু নতুন পিন ক\'ড লিখক" + "PUK ক\'ড" + "নতুন পিন ক\'ড" + " পাছৱর্ড লিখিবলৈ টিপক" + "আনলক কৰিবলৈ পাছৱৰ্ড লিখক" + "আনলক কৰিবলৈ পিন লিখক" + "ভুল পিন ক\'ড।" + "আনলক কৰিবলৈ মেনু টিপাৰ পিছত ০ টিপক।" + "জৰুৰীকালীন নম্বৰ" + "কোনো সেৱা নাই" + "স্ক্ৰীণ লক কৰা হ\'ল।" + "আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।" + "আনলক কৰিবলৈ মেনু টিপক।" + "আনলক কৰিবলৈ আর্হি আঁকক" + + + "কললৈ উভতি যাওক" + "শুদ্ধ!" + "আকৌ চেষ্টা কৰক" + "আকৌ চেষ্টা কৰক" + "সকলো সুবিধা আৰু ডেটাৰ বাবে আনলক কৰক" + "গৰাকীৰ মুখাৱয়বৰ দ্বাৰা আনলক কৰা সর্বধিক সীমা অতিক্ৰম কৰা হ\'ল" + "কোনো ছিম কাৰ্ড নাই" + "টে\'বলেটত ছিম কার্ড নাই।" + "টিভিত ছিম কার্ড নাই।" + "ফ\'নত ছিম কার্ড নাই।" + "এখন ছিম কাৰ্ড ভৰাওক।" + "ছিম কাৰ্ডখন নাই বা পঢ়িব পৰা নগ\'ল। এখন ছিম কাৰ্ড ভৰাওক।" + "ব্যৱহাৰৰ অযোগ্য ছিম কাৰ্ড।" + "আপোনাৰ ছিম কাৰ্ডখন স্থায়ীভাৱে অক্ষম হৈছে।\n অন্য এখন ছিমৰ বাবে আপোনাৰ ৱায়াৰলেছ সেৱা প্ৰদানকাৰীৰ সৈতে যোগাযোগ কৰক।" + "পূৰ্বৱৰ্তী ট্ৰেক" + "পৰৱৰ্তী ট্ৰেক" + "পজ কৰক" + "প্লে কৰক" + "বন্ধ কৰক" + "ৰিৱাইণ্ড কৰক" + "ফাষ্ট ফৰৱাৰ্ড" + "জৰুৰীকালীন কল মাত্ৰ" + "নেটৱর্ক অৱৰোধিত" + "PUKৰ দ্বাৰা লক কৰা ছিম কার্ড।" + "ব্যৱহাৰকাৰীৰ নিৰ্দেশনা চাওক বা গ্ৰাহক সেৱা কেন্দ্ৰৰ সৈতে যোগাযোগ কৰক।" + "ছিম কাৰ্ড লক কৰা হৈছে।" + "ছিম কার্ড আনলক কৰি থকা হৈছে…" + "আপুনি অশুদ্ধভাৱে আপোনাৰ আনলক আৰ্হি %1$d বাৰ আঁকিছে। \n\n %2$d ছেকেণ্ডৰ পিছত পুনৰ চেষ্টা কৰক।" + "আপুনি অশুদ্ধভাৱে আপোনাৰ পাছৱৰ্ড %1$d বাৰ লিখিছে। \n\n%2$d ছেকেণ্ডৰ পিছত পুনৰ চেষ্টা কৰক।" + "আপুনি অশুদ্ধভাৱে আপোনাৰ পিন %1$d বাৰ লিখিছে। \n\n%2$d ছেকেণ্ডৰ পিছত পুনৰ চেষ্টা কৰক।" + "আপুনি অশুদ্ধভাৱে আপোনাৰ লক খোলাৰ আৰ্হিটো %1$d বাৰ আঁকিলে৷ %2$d তকৈ অধিকবাৰ অসফলভাৱে কৰা প্ৰয়াসৰ পিছত, আপোনাৰ ফ\'নটো আনলক কৰিবৰ বাবে Google ছাইন ইনৰ জৰিয়তে কাৰ্যটো কৰিবলৈ কোৱা হ\'ব৷\n\n %3$d ছেকেণ্ডৰ পিছত পুনৰ চেষ্টা কৰক৷" + "আপুনি অশুদ্ধভাৱে আপোনাৰ আনলক আৰ্হি %1$d বাৰ দিলে। %2$dটা অসফল প্ৰয়াসৰ পিছত আপোনাক আপোনাৰ টিভিটো আনলক কৰিবৰ বাবে Google ছাইন ইন ব্যৱহাৰ কৰিবলৈ কোৱা হ\'ব।\n\n %3$d ছেকেণ্ডৰ পিছত পুনৰ চেষ্টা কৰক।" + "আপুনি অশুদ্ধভাৱে আপোনাৰ লক খোলাৰ আৰ্হিটো %1$d বাৰ আঁকিলে৷ %2$d তকৈ অধিকবাৰ অসফলভাৱে কৰা প্ৰয়াসৰ পিছত, আপোনাৰ ফ\'নটো আনলক কৰিবৰ বাবে Google ছাইন ইনৰ জৰিয়তে কাৰ্যটো কৰিবলৈ কোৱা হ\'ব৷\n\n %3$d ছেকেণ্ডৰ পিছত পুনৰ চেষ্টা কৰক৷" + "আপুনি টে\'বলেটটো %1$d বাৰ ভুলকৈ আনলক কৰিবলৈ প্ৰয়াস কৰিছে। %2$d বাৰতকৈ বেছি প্ৰয়াস কৰিলে টে\'বলেটটো ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব আৰু সকলো ব্যৱহাৰকাৰী ডেটা হেৰুৱাব।" + "আপুনি টিভিটো আনলক কৰিবলৈ %1$d বাৰ ভুল প্ৰয়াস কৰিছে আৰু %2$d বাৰ ভুল প্ৰয়াস কৰিলে টিভিটো ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব আৰু আপুনি সকলো ব্যৱহাৰকাৰী ডেটা হেৰুৱাব।" + "আপুনি ফ\'নটো %1$d বাৰ ভুলকৈ আনলক কৰিবলৈ প্ৰয়াস কৰিছে। %2$d বাৰতকৈ বেছি প্ৰয়াস কৰিলে ফ\'নটো ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব আৰু সকলো ব্যৱহাৰকাৰী ডেটা হেৰুৱাব।" + "আপুনি অশুদ্ধভাৱে টে\'বলেটটো আনলক কৰিবলৈ %d বাৰ চেষ্টা কৰিছিল। টে\'বলেটটো এতিয়া ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব।" + "আপুনি টিভিটো আনলক কৰিবলৈ %d বাৰ ভুল প্ৰয়াস কৰিছে। টিভিটো এতিয়া ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব।" + "আপুনি অশুদ্ধভাৱে ফ\'নটো আনলক কৰিবলৈ %d বাৰ চেষ্টা কৰিছিল। ফ\'নটো এতিয়া ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব।" + "%d ছেকেণ্ডৰ পাছত চেষ্টা কৰক।" + "আর্হি পাহৰিলে নেকি?" + "একাউণ্ট আনলক" + "বহুতবাৰ ভুলকৈ আর্হি অঁকা হ\'ল" + "আনলক কৰিবলৈ নিজৰ Google একাউণ্টৰ জৰিয়তে ছাইন ইন কৰক।" + "ব্যৱহাৰকাৰীৰ নাম (ইমেইল)" + "পাছৱৰ্ড" + "ছাইন ইন কৰক" + "ব্যৱহাৰকাৰীৰ নাম আৰু পাছৱর্ড মান্য নহয়।" + "আপোনাৰ ব্যৱহাৰকাৰী নাম আৰু পাছৱর্ড পাহৰিলে নেকি?\n""google.com/accounts/recovery"" চাওক।" + "পৰীক্ষা কৰি থকা হৈছে…" + "আনলক" + "ধ্বনি অন হৈ আছে" + "ধ্বনি অফ হৈ আছে" + "আর্হি অঁকা কার্য আৰম্ভ হ\'ল" + "আর্হি মচি পেলোৱা হ\'ল" + "আৰ্হিত এটা বিন্দু যোগ কৰিছে" + "চেল %1$s যোগ কৰা হ\'ল" + "আর্হি অঁকা সর্ম্পূণ হ\'ল" + "আর্হিৰ ক্ষেত্ৰ।" + "%1$s. ৱিজেট %3$d-ৰ %2$d।" + "ৱিজেট যোগ কৰক।" + "খালী" + "আনলক ক্ষেত্ৰ বিস্তাৰিত হৈছে।" + "আনলক ক্ষেত্ৰ সংকুচিত হৈছে।" + "%1$s ৱিজেট।" + "ব্যৱহাৰকাৰী নিৰ্বাচক" + "স্থিতি" + "কেমেৰা" + "মিডিয়াৰ নিয়ন্ত্ৰণসমূহ" + "ৱিজেটৰ পুনঃক্ৰম আৰম্ভ হ\'ল।" + "ৱিজেটৰ পুনঃক্ৰম সমাপ্ত হ\'ল।" + "ৱিজেট %1$s মচা হ\'ল।" + "আনলক ক্ষেত্ৰ বিস্তাৰ কৰক।" + "শ্লাইডৰদ্বাৰা আনলক।" + "আৰ্হিৰদ্বাৰা আনলক।" + "গৰাকীৰ মুখাৱয়বৰদ্বাৰা আনলক।" + "পিনৰদ্বাৰা আনলক।" + + + + + "পাছৱৰ্ডৰদ্বাৰা আনলক।" + "আৰ্হি ক্ষেত্ৰ।" + "শ্লাইড ক্ষেত্ৰ।" + "?১২৩" + "ABC" + "ALT" + "বর্ণ" + "শব্দ" + "লিংক" + "লাইন" + "ফেক্টৰীৰ পৰীক্ষা কৰিব পৰা নগ\'ল" + "কেৱল /ছিষ্টেম/এপ-ত ইনষ্টল কৰি থোৱা পেকেজৰ বাবেহে FACTORY_TEST কৰিব পৰা যায়।" + "FACTORY_TEST কার্যৰ বাবে কোনো পেকেজ পোৱা নগ\'ল।" + "ৰিবুট কৰক" + "\"%s\"ত পৃষ্ঠাৰ প্ৰত্য়ুত্তৰ:" + "JavaScript" + "নেভিগেশ্বন নিশ্চিত কৰক" + "এই পৃষ্ঠাটোৰ পৰা আঁতৰি যাওক" + "এই পৃষ্ঠাটোতে থাকক" + "%s\n\nআপুনি এই পৃষ্ঠাটো এৰি বেলেগলৈ যাব বিচাৰে নেকি?" + "নিশ্চিত কৰক" + "পৰামৰ্শ: জুম ইন আৰু আউট কৰিবলৈ দুবাৰ টিপক৷" + "স্বয়ংপূৰ্তি" + "স্বয়ংপূৰ্তি ছেট আপ কৰক" + + + " " + "$1$2$3" + ", " + "$1$2$3" + "প্ৰদেশ" + "পিন ক\'ড" + "ৰাজ্য" + "পিন ক\'ড" + "জিলা" + "দ্বীপ" + "জিলা" + "বিভাগ" + "জিলাৰ মুৰব্বী" + "গাঁও" + "ক্ষেত্ৰ" + "এমিৰেট" + "আপোনাৰ ৱেব বুকমার্কবোৰ আৰু ইতিহাস পঢ়ক" + "ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰে ব্যৱহাৰ কৰা সকলো URLৰ ইতিহাস পঢ়িবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিং কৰিব পৰা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।" + "আপোনাৰ ৱেব বুকমার্কবোৰ আৰু ইতিহাস লিখক" + "আপোনাৰ টেবলেটত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিং কৰিব পৰা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।" + "আপোনাৰ টিভিত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিংৰ ক্ষমতা থকা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।" + "আপোনাৰ ফ\'নত সঞ্চয় কৰি ৰখা ব্ৰাউজাৰৰ বুকমার্ক আৰু ব্ৰাউজাৰৰ ইতিহাস সংশোধন কৰিবলৈ এপক অনুমতি দিয়ে। টোকা: এই অনুমতি তৃতীয় পক্ষৰ ব্ৰাউজাৰবোৰ বা ৱেব ব্ৰাউজিং কৰিব পৰা অন্য এপ্লিকেশ্বনবোৰৰ দ্বাৰা বলৱৎ নহ\'বও পাৰে।" + "এলাৰ্ম ছেট কৰক" + "এপটোক ইনষ্টল হৈ থকা এলাৰ্ম ক্লক এপত এলাৰ্ম ছেট কৰিবলৈ অনুমতি দিয়ে। কিছুমান এলাৰ্ম ক্লক এপত এই সুবিধাটো প্ৰযোজ্য নহ\'ব পাৰে।" + "ভইচমেইল যোগ কৰক" + "আপোনাৰ ভইচমেইল ইনবক্সত বাৰ্তাবোৰ যোগ কৰিবলৈ এপটোক অনুমতি দিয়ক।" + "ব্ৰাউজাৰৰ জিঅ\'লোকেশ্বনৰ অনুমতিসমূহ সংশোধন কৰক" + "ব্ৰাউজাৰৰ জিঅ\'লোকেশ্বন বিষয়ক অনুমতিসমূহ সংশোধন কৰিবলৈ এপটোক অনুমতি দিয়ে৷ ক্ষতিকাৰক এপবোৰে একপক্ষীয় ৱেবছাইটসমূহলৈ অৱস্থান সেৱাৰ তথ্য পঠিয়াবলৈ ইয়াক ব্যৱহাৰ কৰিব পাৰে৷" + "ব্ৰাউজাৰে এই পাছৱর্ডটো মনত ৰখাটো বিচাৰেনে?" + "এতিয়াই নহয়" + "মনত ৰাখিব" + "কেতিয়াও মনত নাৰাখিব" + "এই পৃষ্ঠাটো খুলিবলৈ আপোনাৰ অনুমতি নাই।" + "ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ\'ল।" + "অধিক" + "মেনু+" + "স্পেচ" + "লিখক" + "মচক" + "অনুসন্ধান কৰক" + "অনুসন্ধান কৰক…" + "অনুসন্ধান কৰক" + "প্ৰশ্নৰ সন্ধান কৰক" + "প্ৰশ্ন মচক" + "প্ৰশ্ন দাখিল কৰক" + "কণ্ঠধ্বনিৰ যোগেৰে সন্ধান কৰক" + "স্পৰ্শৰে অন্বেষণ কৰা সুবিধা সক্ষম কৰিবনে?" + "%1$sএ স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰিব বিচাৰে। যেতিয়া স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰা সুবিধা অন কৰা থাকে তেতিয়া আপোনাৰ আঙুলিৰ তলত থকা বিৱৰণবোৰ শুনিব বা চাব পাৰে বা আঙুলিৰ সংকেতৰ জৰিয়তে টেবলেট ব্যৱহাৰ কৰিব পাৰে।" + "%1$sএ স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰিব বিচাৰে। যেতিয়া স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰা সুবিধা অন কৰা থাকে তেতিয়া আপোনাৰ আঙুলিৰ তলত থকা বিৱৰণবোৰ শুনিব বা চাব পাৰে বা আঙুলিৰ ইংগিতৰ জৰিয়তে ফ\'ন ব্যৱহাৰ কৰিব পাৰে।" + "১ মাহ আগত" + "১ মাহতকৈও আগত" + + "যোৱা মাহ" + "পুৰণি" + "%s তাৰিখে" + "%s বজাত" + "%s চনত" + "দিন" + "দিনবোৰ" + "ঘণ্টা" + "ঘণ্টা" + "মিনিট" + "কেইমিনিটমান" + "ছেকেণ্ড" + "কেইছেকেণ্ডমান" + "সপ্তাহ" + "কেইসপ্তাহমান" + "বছৰ" + "কেইবছৰমান" + "এতিয়া" + + %d মিনিট + %d মিনিট + + + %d ঘণ্টা + %d ঘণ্টা + + + %d দিন + %d দিন + + + %d বছৰ + %d বছৰ + + + %d মিনিটত + %d মিনিটত + + + %d ঘণ্টাত + %d ঘণ্টাত + + + %d দিনত + %d দিনত + + + %d বছৰত + %d বছৰত + + + %d মিনিটৰ আগত + %d মিনিটৰ আগত + + + %d ঘণ্টাৰ আগত + %d ঘণ্টাৰ আগত + + + %d দিন আগত + %d দিন আগত + + + %d বছৰৰ আগতে + %d বছৰৰ আগতে + + + %d মিনিটত + %d মিনিটত + + + %d ঘণ্টাত + %d ঘণ্টাত + + + %d দিনত + %d দিনত + + + %d বছৰত + %d বছৰত + + "ভিডিঅ\'ত সমস্যা আছে" + "এই ভিডিঅ\'টোক এই ডিভাইচটোত ষ্ট্ৰীমিং কৰিবৰ বাবে মান্য নহয়।" + "এই ভিডিঅ\' প্লে কৰিব পৰা নাযায়।" + "ঠিক আছে" + "%1$s, %2$s" + "দুপৰীয়া" + "দুপৰীয়া" + "মাজনিশা" + "মাজনিশা" + "%1$02d:%2$02d" + "%1$d:%2$02d:%3$02d" + "সকলো বাছনি কৰক" + "কাটক" + "প্ৰতিলিপি কৰক" + "ক্লিপব\'ৰ্ডত প্ৰতিলিপি কৰিব পৰা নগ\'ল" + "পেইষ্ট কৰক" + "নিকা পাঠ হিচাপে পেইষ্ট কৰক" + "সলনি কৰক…" + "মচক" + "URL প্ৰতিলিপি কৰক" + "পাঠ বাছনি কৰক" + "আনডু কৰক" + "ৰিডু" + "স্বয়ংপূৰ্তি" + "বাৰ্তা বাছনি" + "অভিধানত যোগ কৰক" + "মচক" + "ইনপুট পদ্ধতি" + "পাঠ বিষয়ক কাৰ্য" + "ইমেইল" + "কল কৰক" + "অৱস্থান নিৰূপণ কৰক" + "খোলক" + "বাৰ্তা" + "যোগ দিয়ক" + + + + + + + "সঞ্চয়াগাৰৰ খালী ঠাই শেষ হৈ আছে" + "ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে" + "ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এম. বি. খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।" + "%1$s চলি আছে" + "অধিক তথ্য জানিবলৈ বা এপ বন্ধ কৰিবলৈ টিপক।" + "ঠিক আছে" + "বাতিল কৰক" + "ঠিক আছে" + "বাতিল কৰক" + "মনোযোগ দিব" + "ল\'ড কৰি থকা হৈছে…" + "অন কৰক" + "অফ কৰক" + "এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক" + "%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক" + "কাৰ্য সম্পূৰ্ণ কৰক" + "ইয়াৰ জৰিয়তে খোলক" + "%1$sৰ জৰিয়তে খোলক" + "খোলক" + "ইয়াৰ দ্বাৰা সম্পাদনা কৰক" + "%1$sৰদ্বাৰা সম্পাদনা কৰক" + "সম্পাদনা কৰক" + "ইয়াৰ জৰিয়তে শ্বেয়াৰ কৰক" + "%1$sৰ জৰিয়তে শ্বেয়াৰ কৰক" + "শ্বেয়াৰ কৰক" + "ইয়াৰ মাধ্য়মেৰে প্ৰেৰণ কৰক" + "%1$s ব্য়ৱহাৰ কৰি প্ৰেৰণ কৰক" + "প্রেৰণ কৰক" + "এটা হ\'ম এপ্ বাছনি কৰক" + "হ\'ম ৰূপে %1$s ব্যৱহাৰ কৰক" + "প্ৰতিচ্ছবি তোলক" + "ইয়াৰ সৈতে প্ৰতিচ্ছবি তোলক" + "%1$s ৰ সৈতে প্ৰতিচ্ছবি তোলক" + "প্ৰতিচ্ছবি তোলক" + "এই কার্যৰ বাবে পূর্বনির্ধাৰিত ধৰণে ব্যৱহাৰ কৰক।" + "এটা পৃথক এপ্ ব্যৱহাৰ কৰক" + "ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল\'ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷" + "কোনো কার্য বাছনি কৰক" + "ইউএছবি ডিভাইচৰ বাবে এটা এপ্ বাছনি কৰক" + "কোনো এপে এই কাৰ্য কৰিব নোৱাৰে।" + "%1$s বন্ধ হ\'ল" + "%1$s বন্ধ হ\'ল" + "%1$s বাৰে বাৰে বন্ধ হৈ গৈছে" + "%1$s বাৰে বাৰে বন্ধ হৈ গৈছে" + "আকৌ এপটো খোলক" + "আপোনাৰ প্ৰতিক্ৰিয়া পঠিয়াওক" + "বন্ধ কৰক" + "ডিভাইচ ৰিষ্টাৰ্ট নোহোৱালৈ মিউট কৰক" + "অপেক্ষা কৰক" + "এপটো বন্ধ কৰক" + + "%2$sয়ে সঁহাৰি দিয়া নাই" + "%1$sয়ে সঁহাৰি দিয়া নাই" + "%1$sয়ে সঁহাৰি দিয়া নাই" + "%1$s প্ৰক্ৰিয়াই সঁহাৰি দিয়া নাই" + "ঠিক আছে" + "অভিযোগ কৰক" + "অপেক্ষা কৰক" + "এই পৃষ্ঠাই কোনো সহাঁৰি জনোৱা নাই।\n\nআপুনি ইয়াক বন্ধ কৰিব বিচাৰেনে?" + "আন এটা এপৰ ওপৰত" + "এতিয়া %1$s চলি আছে।" + "%1$sক পূৰ্বতে লঞ্চ কৰা হৈছিল৷" + "স্কেল" + "সদায় দেখুৱাওক" + "ছিষ্টেমৰ ছেটিংসমূহ > এপসমূহ > ডাউনল\'ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।" + "%1$sএ বর্তমানৰ ডিছপ্লেৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।" + "সদায় দেখুৱাওক" + "%1$sক এটা খাপ নোখোৱা Android OS সংস্কৰণৰ বাবে তৈয়াৰ কৰা হৈছিল, যাৰ ফলত ই অস্বাভাৱিকধৰণে আচৰণ কৰিব পাৰে। এপটোৰ শেহতীয়া সংস্কৰণ উপলব্ধ হ\'ব পাৰে।" + "সদায় দেখুৱাওক" + "আপডেট আছে নেকি চাওক" + "এপটোৱে %1$s (প্ৰক্ৰিয়াটোৱে %2$s) নিজে বলবৎ কৰা StrictMode নীতি ভংগ কৰিলে।" + "%1$s প্ৰক্ৰিয়াটোৱে নিজে বলৱৎ কৰা StrictMode নীতি ভংগ কৰিলে।" + + + + + + + + + + + + + "সঞ্চয়াগাৰ অপ্টিমাইজ কৰি থকা হৈছে।" + + + "%1$sক আপগ্ৰেড কৰি থকা হৈছে…" + "%2$dৰ ভিতৰত %1$d এপ্ অপ্টিমাইজ কৰি থকা হৈছে৷" + + + "আৰম্ভ হৈ থকা এপসমূহ।" + "বুট কাৰ্য সমাপ্ত কৰিছে।" + "%1$s চলি আছে" + + + + + + + + + + + + + + + "হীপ ডাম্প সংগ্ৰহ কৰা হ\'ল। শ্বেয়াৰ কৰিবলৈ টিপক" + + + + + "বার্তাৰ বাবে কাৰ্য বাছনি কৰক" + "ৰিংগাৰৰ ধ্বনি" + "মিডিয়াৰ ধ্বনি" + "ব্লুটুথৰ জৰিয়তে প্লে কৰি থকা হৈছে" + "ৰিংট\'ন নিৰৱ ম\'ডত ছেট কৰা আছে" + "কলৰ সময়ত ধ্বনিৰ মাত্ৰা" + "ব্লুটুথৰ যোগেৰে কৰা কলৰ সময়ত ধ্বনিৰ মাত্ৰা" + "এলার্মৰ ধ্বনি" + "জাননীৰ ধ্বনি" + "ধ্বনি" + "ব্লুটুথৰ ভলিউম" + "ৰিংট\'নৰ ধ্বনি" + "কলৰ ভলিউম" + "মিডিয়াৰ ভলিউম" + "জাননীৰ ধ্বনি" + "পূর্বনিধার্ৰিত ৰিংট\'ন" + "ডিফ\'ল্ট (%1$s)" + "একো নাই" + "ৰিংট\'নসমূহ" + "এলার্মৰ ধ্বনিসমূহ" + "জাননীৰ ধ্বনিসমূহ" + "অজ্ঞাত" + + ৱাই-ফাই নেটৱর্ক উপলব্ধ + ৱাই-ফাই নেটৱর্ক উপলব্ধ + + + পাছৱৰ্ড অবিহনে সংযোগ কৰিব পৰা ৱাই-ফাই নেটৱর্ক উপলব্ধ + পাছৱৰ্ড অবিহনে সংযোগ কৰিব পৰা ৱাই-ফাই নেটৱর্ক উপলব্ধ + + "পাছৱৰ্ড অবিহনে সংযোগ কৰিবপৰা ৱাই-ফাই নেটৱর্কৰ সৈতে সংযোগ কৰক" + + + "পাছৱৰ্ড অবিহনে সংযোগ কৰিবপৰা ৱাই-ফাই নেটৱর্কৰ সৈতে সংযোগ কৰি থকা হৈছে" + "ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰা হ\'ল" + "ৱাই-ফাই নেটৱৰ্কৰ সৈতে সংযোগ কৰিবপৰা নগ\'ল" + "সকলো নেটৱৰ্ক চাবলৈ টিপক" + "সংযোগ কৰক" + + + "ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন হ\'ব" + "যেতিয়া আপুনি ছেভ কৰি থোৱা উচ্চ মানৰ নেটৱৰ্কৰ কাষত থাকে" + "পুনৰাই অন নকৰিব" + + + + + "ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক" + + + + + "ৱাই-ফাইত ইন্টাৰনেট নাই" + "অধিক বিকল্পৰ বাবে টিপক" + "%1$sলৈ সলনি কৰা হ\'ল" + "যেতিয়া %2$sত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে %1$sক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।" + "%1$sৰ পৰা %2$s লৈ সলনি কৰা হ\'ল" + + "ম\'বাইল ডেটা" + "ৱাই-ফাই" + "ব্লুটুথ" + "ইথাৰনেট" + "ভিপিএন" + + "অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক" + "ৱাই-ফাইৰ লগত সংযোগ কৰিব পৰা নগ\'ল" + " ইণ্টাৰনেট সংযোগ যথেষ্ট দুর্বল।" + "সংযোগ কৰাৰ অনুমতি দিবনে?" + + + "এপ্লিকেশ্বন" + "ৱাই-ফাই ডাইৰেক্ট" + "ৱাই-ফাই ডাইৰেক্ট আৰম্ভ কৰক। এই কার্যই ৱাই-ফাই ক্লাইণ্ট/হ\'টস্প\'ট অফ কৰিব।" + "ৱাই-ফাই ডাইৰেক্ট আৰম্ভ কৰিব পৰা নগ\'ল।" + "ৱাই-ফাই ডাইৰেক্ট অন হৈ আছে" + "ছেটিংসমূহৰ বাবে টিপক" + "স্বীকাৰ কৰক" + "প্ৰত্যাখ্যান কৰক" + "আমন্ত্ৰণ পঠোৱা হ\'ল" + "সংযোগ হ\'বলৈ আমন্ত্ৰণ" + "প্ৰেৰক:" + "প্ৰতি:" + "প্ৰয়োজনীয় পিন নম্বৰটো লিখক:" + "পিন:" + "টি.ভি.টো %1$s লৈ সংযোগ হৈ থকাৰ অৱস্থাত অস্থায়ীভাৱে ৱাই-ফাইৰ পৰা সংযোগ বিচ্ছিন্ন হ\'ব" + "টিভিটো %1$sৰ লগত সংযোগ হ\'লে সাময়িকভাৱে ৱাই-ফাইৰ পৰা সংযোগ বিচ্ছিন্ন হ\'ব" + "ফ\'নটো %1$s লৈ সংযোগ হ\'লে ৱাই-ফাইৰ পৰা কিছু সময়ৰ বাবে সংযোগ স্বীকাৰ বিচ্ছিন্ন হ\'ব" + "বর্ণ লিখক" + "এছএমএছ বার্তাবোৰ পঠিয়াই থকা হৈছে" + "<b>%1$s</b> এ বহু সংখ্যক এছএমএছ বার্তাবোৰ প্ৰেৰণ কৰি আছে। আপুনি এই এপে বার্তা প্ৰেৰণ কৰি থকাটো বিচাৰেনে?" + "অনুমতি দিয়ক" + "অস্বীকাৰ কৰক" + "<b>%1$s</b>এ <b>%2$s</b> লৈ কিবা বার্তা প্ৰেৰণ কৰিব বিচাৰিছে।" + "ইয়াৰ বাবে "" মাছুল ভৰিবলগীয়া হ\'ব পাৰে""।" + "ইয়াৰ বাবে মাছুল ভৰিবলগীয়া হ\'ব পাৰে।" + "পঠিয়াওক" + "বাতিল কৰক" + "মোৰ পচন্দ মনত ৰাখিব" + "আপুনি ইয়াক পিছত ছেটিং > এপ্‌-ত সলনি কৰিব পাৰে" + "যিকোনো সময়ত অনুমতি দিয়ক" + "কেতিয়াও অনুমতি নিদিব" + "ছিম কাৰ্ড আঁতৰোৱা হ\'ল" + "এখন মান্য ছিম কার্ড ব্যৱহাৰ কৰি ৰিষ্টার্ট নকৰা পর্যন্ত ম\'বাইলৰ নেটৱর্ক উপলব্ধ নহয়।" + "সম্পন্ন হ\'ল" + "ছিম কাৰ্ড যোগ কৰা হ\'ল" + "ম\'বাইলৰ নেটৱর্ক ব্যৱহাৰ কৰিবলৈ আপোনাৰ ডিভাইচটো ৰিষ্টার্ট কৰক।" + "ৰিষ্টাৰ্ট কৰক" + + + + + + + + + "নতুন ছিম ভৰোৱা হৈছে" + "ছেট আপ কৰিবলৈ টিপক" + "সময় ছেট কৰক" + "তাৰিখ ছেট কৰক" + "ছেট কৰক" + "সম্পন্ন হ\'ল" + "নতুন: " + "%1$sএ প্ৰদান কৰা" + "কোনো অনুমতিৰ প্ৰয়োজন নাই" + "ইয়াৰ ফলত আপোনাৰ টকা খৰচ হ\'ব পাৰে" + "ঠিক আছে" + + + + + + + + + + + + + + + "অধিক বিকল্পৰ বাবে টিপক।" + + + "এনাল\'গ অডিঅ\' সহায়ক সামগ্ৰী পোৱা গৈছে" + "সংলগ্ন কৰা ডিভাইচটোৱে এই ফ\'নটোৰ সৈতে কাম কৰিব নোৱাৰে। অধিক জানিবলৈ টিপক।" + "ইউএছবি ডিবাগিং সংযোগ কৰা হ\'ল" + "ইউএছবি ডিবাগিং অক্ষম কৰিবলৈ টিপক।" + "ইউএছবি ডিবাগিং অক্ষম কৰিবলৈ বাছনি কৰক।" + "বাগ সম্পর্কীয় অভিযোগ গ্ৰহণ কৰি থকা হৈছে…" + "বাগ সম্পর্কীয় অভিযোগ শ্বেয়াৰ কৰিবনে?" + "বাগ সম্পর্কীয় অভিযোগ শ্বেয়াৰ কৰি থকা হৈছে…" + "আপোনাৰ প্ৰশাসকে এই ডিভাইচটোৰ সমস্যা সমাধানৰ বাবে বাগ সম্পৰ্কীয় অভিযোগ বিচাৰিছে। এপ্ আৰু ডেটা শ্বেয়াৰ কৰা হ\'ব পাৰে।" + "শ্বেয়াৰ কৰক" + "প্ৰত্যাখ্যান কৰক" + "কীব\'ৰ্ড সলনি কৰক" + "কায়িক কীব’ৰ্ড সক্ৰিয় হৈ থাকোঁতে ইয়াক স্ক্ৰীণত ৰাখিব" + "ভাৰ্শ্বুৱল কীব\'ৰ্ড দেখুৱাওক" + "কায়িক কীব’ৰ্ড কনফিগাৰ কৰক" + "ভাষা আৰু চানেকি বাছনি কৰিবলৈ ইয়াত টিপক" + " ABCDEFGHIJKLMNOPQRSTUVWXYZ" + " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "অন্য এপৰ ওপৰত দেখুৱায়" + "%s অন্য এপসমূহৰ ওপৰত প্ৰদৰ্শিত হৈ আছে" + "%sএ অইন এপবোৰৰ ওপৰত প্ৰদৰ্শিত হৈ আছে" + "আপুনি যদি %sএ এই সুবিধাটো ব্যৱহাৰ কৰাটো নিবিচাৰে তেন্তে টিপি ছেটিংসমূহ খোলক আৰু ইয়াক অফ কৰক।" + + + + + "আসোঁৱাহ বিচাৰি থকা হৈছে" + + + + + "%s ব্যৱহাৰযোগ্য নহয়" + "%s ব্যৱহাৰযোগ্য নহয়। সমস্যাটো সমাধান কৰিবলৈ টিপক।" + "%s ব্যৱহাৰযোগ্য হৈ থকা নাই। ঠিক কৰিবলৈ বাছনি কৰক।" + "%sক ব্যৱহাৰ কৰিব নোৱাৰি" + "এই ডিভাইচটোৱে %sক ব্যৱহাৰ কৰিব নোৱাৰে। ব্যৱহাৰ কৰিব পৰা ফৰ্মেটত ছেট আপ কৰিবলৈ টিপক।" + "এই ডিভাইচটোৱে %sক চলাব নোৱাৰে। চলাব পৰা কোনো ফৰ্মেটত ছেট আপ কৰিবলৈ বাছনি কৰক।" + + + + + + + + + + + + + "ছেট আপ কৰক" + + + + + + + + + + + + + + + + + + + + + "আঁতৰোৱা হ\'ল" + "বাহিৰলৈ উলিওৱা হ\'ল" + "পৰীক্ষা কৰি থকা হৈছে…" + "সাজু" + "ৰীড-অনলি" + "বিপজ্জনকভাৱে আঁতৰোৱা হ\'ল" + "ব্যৱহাৰযোগ্য নহয়" + "সঞ্চয়াগাৰ ব্যৱহাৰ কৰিব নোৱাৰি" + "বাহিৰলৈ উলিয়াই থকা হৈছে…" + "ফৰ্মেট কৰি থকা হৈছে…" + "ভৰোৱা নাই" + "কোনো মিলা কাৰ্যকলাপ পোৱা নগ\'ল।" + "মিডিয়াৰ আউটপুট ৰাউট কৰিব পাৰে" + "অন্য় বাহ্যিক ডিভাইচবোৰলৈ মিডিয়া আউটপুট প্ৰেৰণ কৰিবলৈ এপ্লিকেশ্বনক অনুমতি দিয়ে।" + "ইনষ্টল কৰা ছেশ্বনসমূহ পঢ়িব পাৰে" + "এটা এপ্লিকেশ্বনক ইনষ্টল কৰা ছেশ্বনসমূহ পঢ়িবলৈ অনুমতি দিয়ে। এই কাৰ্যই সক্ৰিয় পেকেজ ইনষ্টলেশ্বনৰ বিষয়ে চাবলৈ অনুমতি দিয়ে।" + "পেকেজ ইনষ্টলৰ বাবে অনুৰোধ কৰিব পাৰে" + + + "পেকেজ মচাৰ অনুৰোধ কৰিব পাৰে" + "এপটোক পেকেজবোৰ মচাৰ অনুৰোধ কৰিবলৈ দিয়ে।" + "বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ বিচাৰক" + "কোনো এপক সেই এপটোৰ বাবে বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।" + "জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক" + "ৱিজেট যোগ কৰিব পৰা নগ\'ল।" + "যাওক" + "অনুসন্ধান কৰক" + "পঠিয়াওক" + "পৰৱৰ্তী" + "সম্পন্ন হ\'ল" + "আগৰ" + "কার্য কৰক" + "%s ব্যৱহাৰ কৰি \n নম্বৰটো ডায়েল কৰক" + "%s ব্যৱহাৰ কৰি সম্পৰ্ক \n সৃষ্টি কৰক" + "বৰ্তমান আৰু ভৱিষ্যতে আপোনাৰ একাউণ্টত প্ৰৱেশ কৰিবলৈ তলৰ এটা বা অধিক এপে অনুমতি লাভৰ বাবে অনুৰোধ কৰিছে৷" + "আপুনি এই অনুৰোধক সন্মতি দিব বিচাৰেনে?" + "ব্য়ৱহাৰ কৰাৰ অনুমতি বিচাৰি কৰা অনুৰোধ" + "অনুমতি দিয়ক" + "প্ৰত্যাখ্যান কৰক" + "অনুমতি বিচাৰি অনুৰোধ কৰা হৈছে" + "%s একাউণ্টৰ বাবে\nঅনুমতি বিচাৰি অনুৰোধ কৰা হৈছে।" + "আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ বাহিৰত এই এপটো ব্যৱহাৰ কৰি আছে" + "আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলৰ ভিতৰত এই এপটো ব্যৱহাৰ কৰি আছে" + "ইনপুট পদ্ধতি" + "ছিংক" + "দিব্যাংগসকলৰ বাবে থকা সুবিধাসমূহ" + "ৱালপেপাৰ" + "ৱালপেপাৰ সলনি কৰক" + "জাননী নিৰীক্ষক" + "VR শ্ৰোতা" + "অৱস্থা প্ৰদানকাৰী" + "জাননীৰ স্তৰ নিৰ্দ্ধাৰক সেৱা" + "ভিপিএন সক্ৰিয় কৰা হৈছে" + "%sএ ভিপিএন সক্ৰিয় কৰিলে" + "নেটৱর্ক পৰিচালনা কৰিবলৈ টিপক।" + "%sৰ সৈতে সংযোগ হৈছে। নেটৱর্ক পৰিচালনা কৰিবলৈ টিপক।" + "সদা-সক্ৰিয় ভিপিএন সংযোগ কৰি থকা হৈছে…" + "সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ\'ল" + "সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ বিচ্ছিন্ন কৰা হৈছে" + "সদা-সক্ৰিয় ভিপিএনত আসোঁৱাহ" + "নেটৱৰ্ক বা ভিপিএন ছেটিংসমূহ সলনি কৰক" + "ফাইল বাছনি কৰক" + "কোনো ফাইল বাছনি কৰা হোৱা নাই" + "ৰিছেট কৰক" + "দাখিল কৰক" + "গাড়ীৰ ম’ড সক্ষম কৰা হ\'ল" + "গাড়ীৰ ম\'ডৰ পৰা বাহিৰ হ\'বলৈ টিপক।" + "টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে" + "ছেট আপ কৰিবলৈ টিপক।" + "টেডাৰিং অক্ষম কৰি থোৱা হৈছে" + "সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক" + "উভতি যাওক" + "পৰৱৰ্তী" + "এৰি যাওক" + "কোনো মিল নাই" + "পৃষ্ঠাত বিচাৰক" + + %d%dটা + %d%dটা + + "সম্পন্ন হ\'ল" + "ইউএছবি সঞ্চয়াগাৰৰ ডেটা মচি থকা হৈছে…" + "এছডি কাৰ্ডৰ ডেটা মচি থকা হৈছে…" + "শ্বেয়াৰ কৰক" + "বিচাৰক" + "ৱেবত সন্ধান কৰক" + "পৰৱৰ্তী বস্তু বিচাৰক" + "আগৰটো বিচাৰক" + "%sৰ পৰা অৱস্থানৰ অনুৰোধ কৰা হৈছে" + "অৱস্থানৰ অনুৰোধ" + "%1$s (%2$s)এ অনুৰোধ কৰিছে" + "হয়" + "নহয়" + "মচি পেলোৱাৰ সীমা পাৰ হ\'ল" + "এই %3$s ৰ মচি থোৱা %1$d টা %2$s বস্তু আছে। আপুনি কি কৰিব বিচাৰে?" + "বস্তুবোৰ মচক" + "মচা কাৰ্যক আনডু কৰক" + "এতিয়া একো নকৰিব" + "একাউণ্ট বাছনি কৰক" + "একাউণ্ট যোগ কৰক" + "একাউণ্ট যোগ কৰক" + "বৃদ্ধি কৰক" + "হ্ৰাস কৰক" + "%s স্পর্শ কৰক আৰু হেঁচি ধৰক।" + "বৃদ্ধি কৰিবলৈ ওপৰলৈ আৰু হ্ৰাস কৰিবলৈ তললৈ শ্লাইড কৰক।" + "মিনিট বৃদ্ধি কৰক" + "মিনিট হ্ৰাস কৰক" + "ঘণ্টা বৃদ্ধি কৰক" + "ঘণ্টা হ্ৰাস কৰক" + "অপৰাহ্ন ছেট কৰক" + "পূৰ্বাহ্ন ছেট কৰক" + "মাহ বৃদ্ধি কৰক" + "মাহ হ্ৰাস কৰক" + "দিন বৃদ্ধি কৰক" + "দিন হ্ৰাস কৰক" + "বছৰ বৃদ্ধি কৰক" + "বছৰ হ্ৰাস কৰক" + + + + + "Alt" + "বাতিল কৰক" + "মচক" + "সম্পন্ন হ\'ল" + "ম\'ড সলনি" + "শ্বিফ্ট" + "এণ্টাৰ" + "এটা এপ্ বাছনি কৰক" + "%s লঞ্চ কৰিব পৰা নগ\'ল" + "ইয়াৰ জৰিয়তে শ্বেয়াৰ কৰক" + "%sৰ জৰিয়তে শ্বেয়াৰ কৰক" + "শ্লাইড কৰা হেণ্ডেল৷ স্পৰ্শ কৰক আৰু ধৰি ৰাখক৷" + "স্ক্ৰীণ আনলক কৰিবলৈ ছোৱাইপ কৰক৷" + "গৃহ পৃষ্ঠালৈ যাওক" + "ওপৰলৈ যাওক" + "অধিক বিকল্প" + "%1$s: %2$s" + "%1$s, %2$s, %3$s" + "শ্বেয়াৰ কৰা আভ্যন্তৰীণ সঞ্চয়াগাৰ" + "এছডি কাৰ্ড" + + + + + + + "ইউএছবি সঞ্চয়াগাৰ" + "সম্পাদনা কৰক" + + + + + "ম\'বাইল ডেটা ব্যৱাহৰৰ সীমা শেষ হৈছে" + "ৱাই-ফাই ডেটাৰ সীমাত উপনীত হৈছে" + + + + + + + + + "নেপথ্য ডেটা সীমিত কৰি ৰখা হৈছে৷" + "সীমাবদ্ধতা আঁতৰাবলৈ টিপক।" + + + + + + + "নিৰাপত্তা সম্পৰ্কীয় প্ৰমাণপত্ৰ" + "এই প্ৰমাণপত্ৰখন মান্য৷" + "প্ৰদান কৰা হৈছে:" + "সাধাৰণ নাম:" + "সংস্থা:" + "সংস্থাৰ গোট:" + "এয়া প্ৰদান কৰে:" + "মান্যতা:" + "প্ৰদান কৰা সময়:" + "ম্যাদ উকলিব:" + "ক্ৰমিক নম্বৰ:" + "ফিংগাৰপ্ৰিণ্ট:" + "SHA-256 ফিংগাৰপ্ৰিণ্ট:" + "SHA-1 ফিংগাৰপ্ৰিণ্ট:" + "সকলো চাওক" + "কাৰ্যকলাপ বাছনি কৰক" + "ইয়াৰ জৰিয়তে শ্বেয়াৰ কৰক" + "পঠিয়াই থকা হৈছে…" + "ব্ৰাউজাৰ লঞ্চ কৰিবনে?" + "কল স্বীকাৰ কৰিবনে?" + "সদায়" + "মাত্ৰ এবাৰ" + "%1$sএ কৰ্মস্থানৰ প্ৰ\'ফাইল সমৰ্থন নকৰে।" + "টেবলেট" + "টিভি" + "ফ\'ন" + "ড\'ক স্পীকাৰসমূহ" + "HDMI" + "হেডফ\'নবোৰ" + "ইউএছবি" + "ছিষ্টেম" + "ব্লুটুথ অডিঅ\'" + "ৱায়াৰলেচ ডিছপ্লে\'" + "কাষ্ট" + "ডিভাইচৰ লগত সংযোগ কৰক" + "ডিভাইচত স্ক্ৰীণ কাষ্ট কৰক" + "ডিভাইচৰ সন্ধান কৰক…" + "ছেটিংসমূহ" + "বিচ্ছিন্ন কৰক" + "স্কেন কৰি থকা হৈছে…" + "সংযোগ কৰি থকা হৈছে..." + "উপলব্ধ" + "উপলব্ধ নহয়" + "ব্যৱহাৰ হৈ আছে" + "অন্তৰ্নিমিত স্ক্ৰীণ" + "HDMI স্ক্ৰীণ" + "অ\'ভাৰলে\' #%1$d" + "%1$s: %2$dx%3$d, %4$d dpi" + ", সুৰক্ষিত" + "আৰ্হি পাহৰিলেনে" + "ভুল আৰ্হি" + "ভুল পাছৱৰ্ড" + "ভুল পিন" + + %d ছেকেণ্ডত আকৌ চেষ্টা কৰক। + %d ছেকেণ্ডত আকৌ চেষ্টা কৰক। + + "নিজৰ আৰ্হি আঁকক" + "ছিমৰ পিন দিয়ক" + "পিন দিয়ক" + "পাছৱৰ্ড দিয়ক" + "ছিমখন বর্তমান অক্ষম অৱস্থাত আছে। অব্যাহত ৰাখিবলৈ PUK ক\'ড লিখক। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।" + "ইচ্ছা কৰা পিন ক\'ড লিখক" + "ইচ্ছা কৰা পিন ক\'ড নিশ্চিত কৰক" + "ছিম কার্ড আনলক কৰি থকা হৈছে…" + "ভুল পিন ক\'ড।" + "৪ ৰ পৰা ৮ টা লৈকে সংখ্য়া সন্নিবিষ্ট হোৱা পিন লিখক।" + "PUK ক\'ড ৮টা সংখ্যাৰ হ\'ব লাগিব।" + "শুদ্ধ PUK ক\'ডটো পুনৰ দিয়ক। বাৰে বাৰে ভুল ক\'ড দিলে ছিমখন স্থায়ীভাৱে অক্ষম হ\'ব।" + "পিন ক\'ড মিলা নাই" + "বহুতবাৰ ভুলকৈ আর্হি অঁকাৰ প্ৰয়াস কৰা হৈছে" + "আনলক কৰিবলৈ নিজৰ Google একাউণ্টৰ জৰিয়তে ছাইন ইন কৰক।" + "ব্যৱহাৰকাৰী (ইমেইল)" + "পাছৱৰ্ড" + "ছাইন ইন কৰক" + "ব্যৱহাৰকাৰীৰ অমান্য নাম বা পাছৱর্ড।" + "নিজৰ ব্যৱহাৰকাৰী নাম আৰু পাছৱর্ড পাহৰিলেনে?\n""google.com/accounts/recovery"" লৈ যাওক।" + "একাউণ্ট পৰীক্ষা কৰি থকা হৈছে…" + "আপুনি আপোনাৰ পিন %1$dবাৰ ভুলকৈ লিখিছে। \n\n%2$dছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।" + "আপুনি আপোনাৰ পাছৱৰ্ড %1$dবাৰ ভুলকৈ লিখিছে। \n\n%2$d ছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।" + "আপুনি আপোনাৰ ল\'ক খোলাৰ আৰ্হি %1$dবাৰ ভুলকৈ আঁকিছে। \n\n%2$dছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।" + "আপুনি %1$dবাৰ ভুলকৈ টেবলেটৰ ল\'ক খোলাৰ প্ৰয়াস কৰিছে। %2$dতকৈ বেছি বাৰ ভুল প্ৰয়াস কৰিলে টেবলেটটো ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব আৰু সকলো ব্যৱহাৰকাৰীৰ ডেটা হেৰুৱাব।" + "আপুনি টিভিটো আনলক কৰিবলৈ %1$d বাৰ ভুল প্ৰয়াস কৰিছে আৰু %2$d বাৰ ভুল প্ৰয়াস কৰিলে টিভিটো ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব আৰু আপুনি সকলো ব্যৱহাৰকাৰী ডেটা হেৰুৱাব।" + "আপুনি %1$dবাৰ ভুলকৈ ফ\'নৰ ল\'ক খোলাৰ প্ৰয়াস কৰিছে। %2$dতকৈ বেছি বাৰ ভুল প্ৰয়াস কৰিলে ফ\'নটো ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব আৰু সকলো ব্যৱহাৰকাৰীৰ ডেটা হেৰুৱাব।" + "আপুনি %dবাৰ ভুলকৈ টেবলেটৰ ল\'ক খোলাৰ প্ৰয়াস কৰিছে। টেবলেটটো এতিয়া ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব।" + "আপুনি টিভিটো আনলক কৰিবলৈ %d বাৰ ভুল প্ৰয়াস কৰিছে। টিভিটো এতিয়া ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব।" + "আপুনি %dবাৰ ভুলকৈ ফ\'নৰ ল\'ক খোলাৰ প্ৰয়াস কৰিছে। ফ\'নটো এতিয়া ফেক্টৰী ডিফ\'ল্টলৈ ৰিছেট কৰা হ\'ব।" + "আপুনি আপোনাৰ ল\'ক খোলাৰ আৰ্হিটো %1$dবাৰ ভুলকৈ আঁকিছে। %2$dতকৈ বেছি বাৰ ভুল আৰ্হি আঁকিলে আপোনাৰ টেবলেটটো কোনো একাউণ্টৰ জৰিয়তে আনলক কৰিবলৈ কোৱা হ\'ব।\n\n %3$d ছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।" + "আপুনি টিভিটো আনলক কৰিবলৈ %1$d বাৰ ভুল প্ৰয়াস কৰিছে আৰু %2$d বাৰ ভুল প্ৰয়াস কৰিলে আপোনাক এটা ইমেইল একাউণ্ট ব্যৱহাৰ কৰি আপোনাৰ টিভিটো আনলক কৰিবলৈ কোৱা হ\'ব।\n\n %3$d ছেকেণ্ডত আকৌ চেষ্টা কৰক।" + "আপুনি আপোনাৰ ল\'ক খোলাৰ আৰ্হিটো %1$dবাৰ ভুলকৈ আঁকিছে। %2$dতকৈ বেছি বাৰ ভুল আৰ্হি আঁকিলে আপোনাৰ ফ\'নটো কোনো একাউণ্টৰ জৰিয়তে আনলক কৰিবলৈ কোৱা হ\'ব।\n\n %3$d ছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।" + " — " + "আঁতৰাওক" + "অনুমোদিত স্তৰতকৈ ওপৰলৈ ভলিউম বঢ়াব নেকি?\n\nদীৰ্ঘ সময়ৰ বাবে উচ্চ ভলিউমত শুনাৰ ফলত শ্ৰৱণ ক্ষমতাৰ ক্ষতি হ\'ব পাৰে।" + "দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?" + "শ্বৰ্টকাট অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটামত ৩ ছেকেণ্ডৰ বাবে ছাপ দি থাকিলে দিব্যাংগসকলৰ বাবে থকা সুবিধা এটা আৰম্ভ হ\'ব। \n\n চলিত দিব্যাংগসকলৰ সুবিধা:\n %1$s\n\n আপুনি এই সুবিধাটো ছেটিংসমূহ > দিব্যাংগসকলৰ বাবে সুবিধা-লৈ গৈ সলনি কৰিব পাৰে।" + "শ্বৰ্টকাট অফ কৰক" + "শ্বৰ্টকাট ব্যৱহাৰ কৰক" + "ৰং বিপৰীতকৰণ" + "ৰং শুধৰণী" + "দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাটটোৱে %1$sক অন কৰিছে" + "দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাটটোৱে %1$sক অফ কৰিছে" + "আপুনি দিব্যাংগসকলৰ সুবিধাৰ বুটামটো টিপিলে ব্যৱহাৰ কৰিবলগীয়া কোনো সুবিধা বাছক:" + "সুবিধাসমূহ সলনি কৰিবলৈ দিব্যাংগসকলৰ সুবিধাৰ বুটামটো স্পৰ্শ কৰি থাকক।" + "বিবৰ্ধন" + "বৰ্তমানৰ ব্যৱহাৰকাৰী %1$s।" + "%1$sলৈ সলনি কৰি থকা হৈছে…" + "%1$sৰ পৰা লগ আউট কৰি থকা হৈছে…" + "গৰাকী" + "আসোঁৱাহ" + "আপোনাৰ প্ৰশাসকে এই অৱস্থাটো সলনি কৰিবলৈ অনুমতি নিদিয়ে" + "এই কাৰ্যটো পৰিচালনা কৰিবলৈ কোনো এপ্লিকেশ্বন পোৱা নগ\'ল" + "প্ৰত্যাহাৰ কৰক" + "ISO A0" + "ISO A1" + "ISO A2" + "ISO A3" + "ISO A4" + "ISO A5" + "ISO A6" + "ISO A7" + "ISO A8" + "ISO A9" + "ISO A10" + "ISO B0" + "ISO B1" + "ISO B2" + "ISO B3" + "ISO B4" + "ISO B5" + "ISO B6" + "ISO B7" + "ISO B8" + "ISO B9" + "ISO B10" + "ISO C0" + "ISO C1" + "ISO C2" + "ISO C3" + "ISO C4" + "ISO C5" + "ISO C6" + "ISO C7" + "ISO C8" + "ISO C9" + "ISO C10" + "লেটাৰ" + "চৰকাৰী পত্ৰ" + "লিগেল" + "জুনিয়ৰ লিগেল" + "লেজাৰ" + "টে\'বলইড" + "ইনডেক্স কাৰ্ড ৩x৫" + "ইনডেক্স কাৰ্ড ৪x৬" + "ইনডেক্স কাৰ্ড ৫x৮" + "ম\'নাৰ্ক" + "কুৱাট্ৰো" + "ফুলস্কেপ" + "ROC 8K" + "ROC 16K" + "PRC 1" + "PRC 2" + "PRC 3" + "PRC 4" + "PRC 5" + "PRC 6" + "PRC 7" + "PRC 8" + "PRC 9" + "PRC 10" + "PRC 16K" + "Pa Kai" + "Dai Pa Kai" + "Jurro Ku Kai" + "JIS B10" + "JIS B9" + "JIS B8" + "JIS B7" + "JIS B6" + "JIS B5" + "JIS B4" + "JIS B3" + "JIS B2" + "JIS B1" + "JIS B0" + "JIS Exec" + "Chou4" + "Chou3" + "Chou2" + "Hagaki" + "Oufuku" + "Kahu" + "Kaku2" + "You4" + "অজ্ঞাত প\'ৰ্ট্ৰেইট" + "অজ্ঞাত লেণ্ডস্কেইপ" + "বাতিল কৰা হ\'ল" + "সমল লিখাত আসোঁৱাহ" + "অজ্ঞাত" + "প্ৰিণ্টিং সেৱা সক্ষম নহয়" + "%s সেৱা ইনষ্টল কৰা হ\'ল" + "সক্ষম কৰিবলৈ টিপক" + "প্ৰশাসকৰ পিন দিয়ক" + "পিন দিয়ক" + "অশুদ্ধ" + "বৰ্তমানৰ পিন" + "নতুন পিন" + "নতুন পিন নিশ্চিত কৰক" + "সীমাবদ্ধতা সংশোধন কৰিবলৈ এটা পিন সৃষ্টি কৰক" + "পিনবোৰ মিলা নাই। আকৌ চেষ্টা কৰক।" + "পিনটো অতি চুটি। কমেও ৪টা সংখ্যাৰ হ\'ব লাগিব।" + + %d ছেকেণ্ডত আকৌ চেষ্টা কৰক + %d ছেকেণ্ডত আকৌ চেষ্টা কৰক + + "পিছত আকৌ চেষ্টা কৰক" + + + + + + + "সম্পন্ন কৰা হ\'ল" + "ঘড়ীৰ বৃত্তাকাৰ শ্লাইডাৰ" + "মিনিটৰ বৃত্তাকাৰ শ্লাইডাৰ" + "ঘণ্টা বাছনি কৰক" + "মিনিট বাছনি কৰক" + "মাহ আৰু দিন বাছনি কৰক" + "বছৰ বাছনি কৰক" + "%1$s মচা হ\'ল" + "কৰ্মস্থান %1$s" + "২য় কার্য %1$s" + "৩য় কার্য %1$s" + "আনপিন কৰাৰ পূৰ্বে পিন দিবলৈ কওক" + "আনপিন কৰাৰ পূৰ্বে আনলক আৰ্হি দিবলৈ কওক" + "আনপিন কৰাৰ পূৰ্বে পাছৱৰ্ড দিবলৈ কওক" + "আপোনাৰ প্ৰশাসকে ইনষ্টল কৰিছে" + "আপোনাৰ প্ৰশাসকে আপেডট কৰিছে" + "আপোনাৰ প্ৰশাসকে মচিছে" + "বেটাৰিৰ অৱস্থা উন্নত কৰাত সহায় কৰিবলৈ বেটাৰি সঞ্চয়কাৰী সুবিধাই আপোনাৰ ডিভাইচৰ কাৰ্যদক্ষতা হ্ৰাস কৰে আৰু কম্পন, অৱস্থান সেৱা আৰু অধিকাংশ নেপথ্য ডেটা সীমিত কৰে। ছিংকৰ ওপৰত নির্ভৰশীল ইমেইল, মেছেজিং আৰু অন্য এপসমূহ আপুনি নোখোলা পৰ্যন্ত আপডেট নহ\'বও পাৰে।\n\nআপোনাৰ ডিভাইচ চ্চার্জ কৰি থকাৰ সময়ত বেটাৰি সঞ্চয়কাৰী সুবিধা স্বয়ংক্ৰিয়ভাৱে অফ হ\'ব।" + "ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ\'ব।" + "ডেটা সঞ্চয়কাৰী অন কৰিবনে?" + "অন কৰক" + + %1$d মিনিটৰ বাবে (%2$s পৰ্যন্ত) + %1$d মিনিটৰ বাবে (%2$s পৰ্যন্ত) + + + + %1$d ঘণ্টাৰ বাবে (%2$s পৰ্যন্ত) + %1$d ঘণ্টাৰ বাবে (%2$s পৰ্যন্ত) + + + + %d মিনিটৰ বাবে + %d মিনিটৰ বাবে + + + + %d ঘণ্টাৰ বাবে + %d ঘণ্টাৰ বাবে + + + "%1$s পৰ্যন্ত" + "%1$s (পৰৱৰ্তী এলার্ম) পর্যন্ত" + + + + + + + "সংকুচিত কৰক" + + + + + + + + + + + "নিদ্ৰাৰত" + "%1$sৰ দ্বাৰা মিউট কৰা হৈছে" + "আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।" + "আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে। সবিশেষ জানিবৰ বাবে আপোনাৰ ডিভাইচ নির্মাতাৰ সৈতে যোগাযোগ কৰক।" + "USSD অনুৰোধক DIAL অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "USSD অনুৰোধক SS অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "USSD অনুৰোধক নতুন USSD অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "USSD অনুৰোধক ভিডিঅ\' DIAL অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "SS অনুৰোধক DIAL অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "SS অনুৰোধক ভিডিঅ\' DIAL অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "SS অনুৰোধক USSD অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "SS অনুৰোধক নতুন SS অনুৰোধলৈ সংশোধিত কৰা হৈছে।" + "কৰ্মস্থানৰ প্ৰ\'ফাইল" + "বিস্তাৰ কৰক" + "সংকুচিত কৰক" + "সম্প্ৰসাৰণ ট’গল কৰক" + + + + + + + + + + + "সৰ্বাধিক মাত্ৰালৈ বঢ়াওক" + "বন্ধ কৰক" + "%1$s: %2$s" + + %1$dটা বাছনি কৰা হ\'ল + %1$dটা বাছনি কৰা হ\'ল + + "শ্ৰেণীবদ্ধ নকৰা" + "এই জাননীবোৰৰ গুৰুত্ব আপুনি ছেট কৰব লাগিব।" + "এই কার্যৰ সৈতে জড়িত থকা লোকসকলক ভিত্তি কৰি এইয়া গুৰুত্বপূর্ণ বুলি বিবেচনা কৰা হৈছ।" + "%1$s%2$sৰ জৰিয়তে নতুন ব্য়ৱহাৰকাৰী সৃষ্টি কৰিবলৈ অনুমতি দিবনে?" + "%1$s%2$sৰ (এই একাউন্টৰ এজন ব্য়ৱহাৰকাৰী ইতিমধ্য়ে আছে) জৰিয়তে নতুন ব্য়ৱহাৰকাৰী সৃষ্টি কৰিবলৈ অনুমতি দিবনে?" + "ভাষা এটা যোগ কৰক" + "অঞ্চলৰ অগ্ৰাধিকাৰ" + "ভাষাৰ নাম লিখক" + "প্ৰস্তাৱিত" + "সকলো ভাষা" + "সকলো অঞ্চল" + "অনুসন্ধান কৰক" + + + + + "অন কৰক" + + + + + "আপুনি নতুন বার্তা লাভ কৰিছে" + "চাবলৈ এছএমএছ এপ্ খোলক" + "কিছুমান কৰ্মক্ষমতা সীমিত হ\'ব পাৰে" + "আনলক কৰিবলৈ টিপক" + "ব্য়ৱহাৰকাৰীৰ ডেটা লক হৈ আছে" + "কৰ্মস্থানৰ প্ৰ’ফাইল লক হৈ আছে" + "কৰ্মস্থানৰ প্ৰ’ফাইল আনলক কৰিবলৈ টিপক" + "%1$sৰ সৈতে সংযুক্ত হৈ আছে" + "ফাইলসমূহ চাবৰ বাবে টিপক" + "পিন" + "আনপিন" + "এপ্ সম্পৰ্কীয় তথ্য" + "−%1$s" + "ডেম\' আৰম্ভ কৰি থকা হৈছে…" + "ডিভাইচটো আকৌ ছেটিং কৰি থকা হৈছে…" + "%1$sক অক্ষম কৰা হ\'ল" + "কনফাৰেঞ্চ কল" + "টুলটিপ" + "গেম" + "সংগীত আৰু ধ্বনি" + "চলচ্চিত্ৰ আৰু ভিডিঅ\'" + "ফট\' আৰু প্ৰতিচ্ছবি" + "সামাজিক আৰু যোগাযোগ" + "বাতৰি আৰু আলোচনী" + "মেপ আৰু দিক্-নিৰ্দেশনা" + "উৎপাদনশীলতা" + "ডিভাইচৰ সঞ্চয়াগাৰ" + "ইউএছবি ডিবাগিং" + "ঘণ্টা" + "মিনিট" + "সময় ছেট কৰক" + "এটা মান্য সময় দিয়ক" + "সময় টাইপ কৰক" + "সময়ৰ ইনপুটৰ বাবে পাঠৰ ইনপুট ম\'ডলৈ যাওক।" + "সময়ৰ ইনপুটৰ বাবে ঘড়ী ম\'ডলৈ যাওক।" + "স্বয়ংপূৰ্তিৰ বিকল্পসমূহ" + "পিছত স্বয়ংপূৰ্তি কৰিবলৈ ছেভ কৰক" + "সমলসমূহ স্বয়ংপূৰ্তি কৰিব নোৱাৰি" + "কোনো স্বয়ংপূৰ্তি পৰামৰ্শ নাই" + + %1$sটা স্বয়ংপূৰ্তি পৰামৰ্শ + %1$sটা স্বয়ংপূৰ্তি পৰামৰ্শ + + "<b>%1$s</b>ত ছেভ কৰিবনে?" + "%1$sক <b>%2$s</b>ত ছেভ কৰিবনে?" + "%1$s আৰু %2$sক <b>%3$s</b>ত ছেভ কৰিবনে?" + "%1$s, %2$s, আৰু %3$sক <b>%4$s</b>ত ছেভ কৰিবনে?" + "ছেভ কৰক" + "নালাগে, ধন্যবাদ" + "পাছৱৰ্ড" + "ঠিকনা" + "ক্ৰেডিট কাৰ্ড" + "ব্যৱহাৰকাৰীৰ নাম" + "ইমেইল ঠিকনা" + "শান্ত হৈ থাকক আৰু ওচৰৰ ক\'ৰবাত আশ্ৰয় বিচাৰক।" + "উপকূলীয় আৰু নদী-কাযৰীয়া অঞ্চলৰ পৰা তৎক্ষণাৎ আঁতৰ হওক আৰু ওখ অঞ্চলৰ নিচিনা নিৰাপদ ঠাইত আশ্ৰয় লওক।" + "শান্ত হৈ থাকক আৰু ওচৰৰ ক\'ৰবাত আশ্ৰয় বিচাৰক।" + "জৰুৰীকালীন বাৰ্তা সম্পৰ্কীয় পৰীক্ষণ" + "উত্তৰ দিয়ক" + + "ভইচৰ বাবে ছিম ব্যৱহাৰৰ অনুমতি নাই" + "ভইচৰ বাবে ছিমৰ প্ৰ\'ভিজন কৰা হোৱা নাই" + "ভইচৰ বাবে ছিম ব্যৱহাৰৰ অনুমতি নাই" + "ভইচৰ বাবে ফ\'ন ব্যৱহাৰৰ অনুমতি নাই" + "পপআপ ৱিণ্ড\'" + "+ %1$d" + + + "এপটোত বেকআপ আৰু পুনঃস্থাপন সুবিধা নথকাৰ বাবে শ্বৰ্টকাট পুনঃস্থাপন কৰিবপৰা নগ\'ল" + "এপৰ স্বাক্ষৰৰ অমিল হোৱাৰ বাবে শ্বৰ্টকাট পুনঃস্থাপন কৰিবপৰা নগ\'ল" + "শ্বৰ্টকাট পুনঃস্থাপন কৰিবপৰা নগ\'ল" + "শ্বৰ্টকাট অক্ষম কৰি থোৱা হৈছে" + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 082a7a64de8050dbf1d147853c4bd4142199d3cf..bd3de481b53092ec27d23a2525c213fd80fe3bb3 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -185,7 +185,7 @@ "Прылада знаходзіцца пад кіраваннем" "Ваша арганізацыя кіруе гэтай прыладай і можа сачыць за сеткавым трафікам. Дакраніцеся для атрымання дадатковай інфармацыі." "Даныя вашай прылады будуць сцерты" - "Немагчыма выкарыстоўваць праграму адміністратара. Зараз звесткі на вашай прыладзе будуць выдалены.\n\nКалі ў вас ёсць пытанні, звярніцеся да адміністратара вашай арганізацыі." + "Немагчыма выкарыстоўваць праграму адміністравання. Звесткі на вашай прыладзе будуць выдалены.\n\nКалі ў вас ёсць пытанні, звярніцеся да адміністратара арганізацыі." "Друк адключаны ўладальнікам праграмы %s." "Я" "Параметры планшэта" @@ -242,6 +242,9 @@ "Рэжым палёту" "Уключаны рэжым \"У самалёце\"" "Рэжым \"У самалёце\" адключаны" + "Эканомія зараду" + "Рэжым эканоміі зараду выключаны" + "Рэжым эканоміі зараду ўключаны" "Налады" "Дапамога" "Галас. дапамога" @@ -311,7 +314,7 @@ "Выконваць жэсты" "Можна кранаць, праводзіць пальцам, маштабаваць шчыпком, а таксама выконваць іншыя жэсты." "Жэсты адбіткаў пальцаў" - "Можа распазнаваць жэсты на сканеры адбіткаў пальцаў прылады." + "Можа распазнаваць жэсты на сканеры адбіткаў пальцаў прылады." "адключаць ці змяняць радок стану" "Дазваляе прыкладанням адключаць радок стану або дадаваць і выдаляць сістэмныя значкі." "быць панэллю стану" @@ -362,6 +365,8 @@ "Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу планшэта." "Дазваляе праграме пастаянна захоўваць некаторыя свае часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых праграм, i запаволiць працу тэлевізара." "Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу тэлефона." + "запусціць асноўныя сэрвісы" + "Дазваляе праграме выкарыстоўваць асноўныя сэрвісы." "вымерыць прастору для захоўвання прыкладання" "Дазваляе прыкладанням атрымліваць яго код, дадзеныя і аб\'ём кэш-памяці" "змена сістэмных налад" @@ -808,6 +813,8 @@ "Узор разблакiроўкі." "Фэйскантроль" "PIN-код разблакiроўкі." + "Разблакіроўка SIM-карты з дапамогай PIN-кода." + "Разблакіроўка SIM-карты з дапамогай PUK-кода." "Пароль разблакiроўкі." "Вобласць узора." "Вобласць слайда." @@ -1114,29 +1121,33 @@ "Праверыць на наяўнасць абнаўленняў" "Прыкладанне %1$s (працэс %2$s) парушыла ўласную палітыку StrictMode." "Працэс %1$s парушыў уласную палітыку StrictMode." - "Абнаўленне Android..." - "Android запускаецца..." + + + + + + + + + + + + "Аптымізацыя сховішча." - "Абнаўленне Android завяршаецца…" - "Пэўныя праграмы могуць не працаваць належным чынам, пакуль не скончыцца абнаўленне" + + "%1$s абнаўляецца…" "Аптымізацыя прыкладання %1$d з %2$d." "Падрыхтоўка %1$s." "Запуск прыкладанняў." "Завяршэнне загрузкі." "Прыкладанне \"%1$s\" запушчанае" - - - - - - - - - - - - + "Націсніце, каб вярнуцца да гульні" + "Выберыце гульню" + "Для больш прадукцыйнай працы прылады адначасова дазваляецца адкрыць толькі адну з гэтых гульняў." + "Вярнуцца да праграмы \"%1$s\"" + "Адкрыць праграму \"%1$s\"" + "Праграма \"%1$s\" будзе закрыта. Даныя не будуць захаваны" "Працэс %1$s перавысіў ліміт памяці" "Быў сабраны дамп кучы; дакраніцеся, каб абагуліць" "Абагуліць дамп дынамічнай вобласці?" @@ -1182,7 +1193,8 @@ "Не атрымалася падключыцца да адкрытай сеткі Wi‑Fi" "Дакраніцеся, каб убачыць усе сеткі" "Падключыцца" - "Усе сеткі" + + "Wi‑Fi уключыцца аўтаматычна" "Побач з захаванай сеткай з высакаякасным сігналам" "Не ўключаць зноў" @@ -1248,6 +1260,7 @@ "Перазапусціць" "Уключыць мабільную сувязь" "Спампаваць праграму аператара для актывацыі новай SIM-карты" + "Для актывацыі новай SIM-карты спампуйце праграму %1$s" "Спампаваць праграму" "Устаўлена новая SIM-карта" "Краніце, каб наладзіць" @@ -1291,7 +1304,7 @@ "%s паказваецца паверх іншых праграм" "%s паказв. паверх іншых праграм" "Калі вы не хочаце, каб праграма %s выкарыстоўвала гэту функцыю, дакраніцеся, каб адкрыць налады і адключыць гэта." - "АДКЛЮЧЫЦЬ" + "Выключыць" "Падрыхтоўка %s" "Праверка на наяўнасць памылак" "Выяўлены новы носьбіт %s" @@ -1464,22 +1477,19 @@ "USB-дыск %s" "USB-назапашвальнік" "Рэдагаваць" - "Абвестка аб выкарыстанні трафіка" - "Прагляд выкарыстання і налад." - "Дасягнуты ліміт трафіку 2G-3G" - "Дасягнуты ліміт трафіку 4G" + "Папярэджанне аб перавышэнні ліміту трафіка" + "Вы выкарысталі %s даных" "Дасягн. ліміт маб. перад. даных" "Дасягн. ліміт перад. даных Wi-Fi" - "Перад.даных спын. да канца цыкла" - "Перавышаны ліміт 2G-3G" - "Перавышаны ліміт дадзеных 4G" - "Перавышаны ліміт мабільных дадзеных" - "Перав. ліміт па дадзеным Wi-Fi" - "Аб\'ём %s перавышае устаноўл. мяжу." + "Выкарыстанне даных прыпынена да канца цыкла" + "Перавышаны ліміт мабільных даных" + "Перавышаны ліміт трафіка Wi-Fi" + "Вы перавысілі ліміт на %s" "Зыходныя дадзеныя абмежаваныя" "Дакраніцеся, каб зняць абмежав." - "Значнае выкарыстанне трафіка" - "У апошнія некалькі дзён вы выкарысталі больш трафіку, чым звычайна. Націсніце для прагляду выкарыстання і налад." + "Інтэнсіўнае выкарыстанне трафіка" + "Праграмы выкарысталі больш даных, чым звычайна" + "У праграме %s было выкарыстана больш даных, чым звычайна" "Сертыфікат бяспекі" "Гэты сертыфікат сапраўдны." "Каму выдадзена:" diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 4f0c0fbc48356b90dfba76a4c2d2b15aa9110152..265eaaf397135e5fabbe4979d694a983e85d3303 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -27,6 +27,9 @@ 2dip 72dp + + @dimen/status_bar_height_landscape + 40dip diff --git a/core/res/res/values-mcc001-mnc01-as/strings.xml b/core/res/res/values-mcc001-mnc01-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..93b8fc7bcefe826ef7f45546cffe3169834111e7 --- /dev/null +++ b/core/res/res/values-mcc001-mnc01-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc302-mnc370-as/strings.xml b/core/res/res/values-mcc302-mnc370-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c0ad316d585328cf950e6621a1daf82908039dae --- /dev/null +++ b/core/res/res/values-mcc302-mnc370-as/strings.xml @@ -0,0 +1,26 @@ + + + + + + "%s" + "%s ৱাই-ফাই" + + diff --git a/core/res/res/values-mcc302-mnc720-as/strings.xml b/core/res/res/values-mcc302-mnc720-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ab4dacd3fa475dd7c794f1adbc4ac53a597b9ca --- /dev/null +++ b/core/res/res/values-mcc302-mnc720-as/strings.xml @@ -0,0 +1,26 @@ + + + + + + "%s" + "%s ৱাই-ফাই" + + diff --git a/core/res/res/values-mcc310-mnc030-as/strings.xml b/core/res/res/values-mcc310-mnc030-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..27953b87c8aa179c2484091b12e426adbb439d2a --- /dev/null +++ b/core/res/res/values-mcc310-mnc030-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc310-mnc150-as/strings.xml b/core/res/res/values-mcc310-mnc150-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..174cfe3f6122839b1793e323d508eb15d5c768d3 --- /dev/null +++ b/core/res/res/values-mcc310-mnc150-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc310-mnc170-as/strings.xml b/core/res/res/values-mcc310-mnc170-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..37b727b6a45f350aefeecf7949c3cfc7ba34256c --- /dev/null +++ b/core/res/res/values-mcc310-mnc170-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc310-mnc280-as/strings.xml b/core/res/res/values-mcc310-mnc280-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3bc3c1f4288d03f2a4dcc1f2a1698d9d9f98e98e --- /dev/null +++ b/core/res/res/values-mcc310-mnc280-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc310-mnc380-as/strings.xml b/core/res/res/values-mcc310-mnc380-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..4c4a705b50dd3c4704f322f64451a6486f1d48a4 --- /dev/null +++ b/core/res/res/values-mcc310-mnc380-as/strings.xml @@ -0,0 +1,25 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + diff --git a/core/res/res/values-mcc310-mnc410-as/strings.xml b/core/res/res/values-mcc310-mnc410-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..94b9726b7cffc049be51db7bd4cc56072fde6d58 --- /dev/null +++ b/core/res/res/values-mcc310-mnc410-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc310-mnc560-as/strings.xml b/core/res/res/values-mcc310-mnc560-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..636519025adf26f04396a3836e57d03e3e97d506 --- /dev/null +++ b/core/res/res/values-mcc310-mnc560-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc310-mnc950-as/strings.xml b/core/res/res/values-mcc310-mnc950-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7a3af72a7a6f0b650b56e5fdb7db6cb418341600 --- /dev/null +++ b/core/res/res/values-mcc310-mnc950-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc311-mnc180-as/strings.xml b/core/res/res/values-mcc311-mnc180-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..868c39b9c72b55995318e047a6426bbde4dcd44b --- /dev/null +++ b/core/res/res/values-mcc311-mnc180-as/strings.xml @@ -0,0 +1,26 @@ + + + + + "ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2" + "ছিমৰ অনুমতি নাই MM#3" + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc312-mnc670-as/strings.xml b/core/res/res/values-mcc312-mnc670-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..534d4e1a8e7157d5930218daafe6835c065c1352 --- /dev/null +++ b/core/res/res/values-mcc312-mnc670-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-mcc313-mnc100-as/strings.xml b/core/res/res/values-mcc313-mnc100-as/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..350757ca43a4a7cc56688bff156f2fdce8c8e095 --- /dev/null +++ b/core/res/res/values-mcc313-mnc100-as/strings.xml @@ -0,0 +1,24 @@ + + + + + "ফ\'নৰ অনুমতি নাই MM#6" + diff --git a/core/res/res/values-or-watch/strings.xml b/core/res/res/values-or-watch/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..4498c958031f1b06dafa2b14152b3f8831564dfa --- /dev/null +++ b/core/res/res/values-or-watch/strings.xml @@ -0,0 +1,25 @@ + + + + + "%2$dଟି ଆପ୍‍ରୁ %1$dଟି।" + "ସେନ୍ସର୍‍" + diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..ccfc523471b03b90edeea080915e7916b2ca4844 --- /dev/null +++ b/core/res/res/values-or/strings.xml @@ -0,0 +1,2133 @@ + + + + + "B" + "kB" + "MB" + "GB" + "TB" + "PB" + "%1$s %2$s" + "<ନାମହୀନ>" + "(କୌଣସି ଫୋନ୍ ନମ୍ବର ନାହିଁ)" + "ଅଜଣା" + "ଭଏସ୍‌ ମେଲ୍" + "MSISDN1" + "ସଂଯୋଗରେ ସମସ୍ୟା ଅଛି କିମ୍ବା ଅମାନ୍ୟ MMI କୋଡ୍।" + "କେବଳ ସ୍ଥାୟୀ ଡାୟଲିଙ୍ଗ ନମ୍ବର୍‌ ପାଇଁ କାର୍ଯ୍ୟ ସୀମିତ ଅଟେ।" + "ଆପଣ ରୋମିଙ୍ଗରେ ଥିବାବେଳେ କଲ୍‍ ଫର୍‌ୱର୍ଡିଙ୍ଗ ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।" + "ସେବା ସକ୍ଷମ କରାଯାଇଥିଲା।" + "ଏଥିପାଇଁ ସେବା ସକ୍ଷମ କରାଯାଇଥିଲା:" + "ସେବାକୁ ଅକ୍ଷମ କରିଦିଆଯାଇଛି।" + "ପଞ୍ଜିକରଣ ସଫଳ ହେଲା।" + "ଲିଭାଇବା ସଫଳ ହେଲା।" + "ଭୁଲ ପାସୱର୍ଡ।" + "MMI ସମ୍ପୂର୍ଣ୍ଣ।" + "ଆପଣ ଟାଇପ୍‍ କରିଥିବା ପୁରୁଣା PIN ଭୁଲ ଅଟେ।" + "ଆପଣ ଟାଇପ୍‍ କରିଥିବା PUK ଭୁଲ ଅଟେ।" + "ଆପଣ ଟାଇପ୍‍ କରିଥିବା PINଗୁଡ଼ିକ ମେଳ ହେଉନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "4 ରୁ 8ଟି ସଂଖ୍ୟା ବିଶିଷ୍ଟ ଏକ PIN ଟାଇପ୍ କରନ୍ତୁ।" + "ଏକ PUK ଟାଇପ୍ କରନ୍ତୁ, ଯାହାକି 8 ସଂଖ୍ୟା ବିଶିଷ୍ଟ କିମ୍ୱା ତା’ଠାରୁ ଅଧିକ ହୋଇଥିବ।" + "ଆପଣଙ୍କ SIM କାର୍ଡ PUK ଲକ୍ ଅଛି। ଏହାକୁ ଅନଲକ୍‍ କରିବାକୁ PUK କୋଡ୍‌ ଟାଇପ୍ କରନ୍ତୁ।" + "SIM କାର୍ଡରୁ ଅବରୋଧ ହଟାଇବା ପାଇଁ PUK2 ଟାଇପ୍ କରନ୍ତୁ।" + "ସଫଳ ହେଲାନାହିଁ, SIM/RUIM ଲକ୍‍ କରନ୍ତୁ।" + + ଆଉ %dଟି ପ୍ରୟାସ ପରେ SIM ଲକ୍‍ ହୋଇଯିବ। + ଆଉ %dଟି ପ୍ରୟାସ ପରେ SIM ଲକ୍‍ ହୋଇଯିବ। + + "IMEI" + "MEID" + "ଇନକମିଙ୍ଗ କଲର୍ ଆଇଡି" + "ଆଉଟଗୋଇଙ୍ଗ୍ କଲର୍ ଆଇଡି" + "ସଂଯୁକ୍ତ ଲାଇନ୍ ID" + "ସଂଯୁକ୍ତ ଲାଇନ୍ ID କଟକଣା" + "କଲ୍‌ ଫରୱାର୍ଡିଙ୍ଗ" + "କଲ୍‌ ଅପେକ୍ଷାରତ" + "କଲ୍‌ ବ୍ୟାରିଙ୍ଗ୍" + "ପାସ୍‌ୱର୍ଡ ପରିବର୍ତ୍ତନ" + "PIN ପରିବର୍ତ୍ତନ" + "କଲିଙ୍ଗ ନମ୍ୱର୍‌ ଉପସ୍ଥିତ" + "କଲିଙ୍ଗ ନମ୍ବର୍‌ ପ୍ରତିବନ୍ଧିତ" + "ତିନି ପ୍ରକାରରେ କଲିଙ୍ଗ" + "ଅବାଞ୍ଛିତ ଅଜଣା କଲ୍‌ଗୁଡ଼ିକର ପ୍ରତ୍ୟାଖ୍ୟାନ" + "କଲିଙ୍ଗ ନମ୍ବର୍ ଡେଲିଭରୀ" + "ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ" + "କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ" + "କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ" + "କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ" + "କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ" + "ସେବାର ସୁବିଧା ନାହିଁ।" + "ଆପଣ କଲର୍‍ ID ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।" + + + + + + + + + + + "ନେଟ୍‌ୱର୍କ ପର୍ଯ୍ୟନ୍ତ ପହଞ୍ଚିପାରୁ ନାହିଁ" + + + + + + + + + + + + + + + + + + + + + + + "ପୀଆର୍‌ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍‍ FULL ଅଟେ" + "ପୀଅର୍‌ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍‍ HCO ଅଟେ" + "ପୀଅର୍‌ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍‍ VCO ଅଟେ" + "ପୀଅର୍‌ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍‍ OFF ଅଛି" + "ଭଏସ୍" + "ଡାଟା" + "ଫାକ୍ସ" + "SMS" + "ଏସିଙ୍କ୍" + "ସିଙ୍କ୍" + "ପ୍ୟାକେଟ୍" + "PAD" + "ରୋମିଙ୍ଗ ସୂଚକ ଅନ୍ ଅଛି" + "ରୋମିଙ୍ଗ ସୂଚକ ଅଫ୍ ଅଛି" + "ରୋମିଙ୍ଗ ସୂଚକ ଧପ୍‍ ଧପ୍‍ ହେଉଛି" + "ନିକଟସ୍ଥ ଅଞ୍ଚଳ ବାହାରେ" + "ବିଲଡିଙ୍ଗ ବାହାରେ" + "ରୋମିଙ୍ଗ - ପସନ୍ଦର ସିଷ୍ଟମ୍" + "ରୋମିଙ୍ଗ - ଉପଲବ୍ଧ ସେବା" + "ରୋମିଙ୍ଗ - ଆଲାୟାନ୍ସ ପାର୍ଟନର୍‍" + "ରୋମିଙ୍ଗ - ପ୍ରିମିୟମ୍ ପାର୍ଟନର୍" + "ରୋମିଙ୍ଗ - ସମ୍ପୂର୍ଣ୍ଣ ସେବା କାର୍ଯ୍ୟକ୍ଷମତା" + "ରୋମିଙ୍ଗ - ଆଂଶିକ ସେବା କାର୍ଯ୍ୟକ୍ଷମତା" + "ରୋମିଙ୍ଗ ବ୍ୟାନର୍ ଅନ୍ ଅଛି" + "ରୋମିଙ୍ଗ ବ୍ୟାନର୍ ଅଫ୍ ଅଛି" + "ସେବା ଖୋଜାଯାଉଛି" + "ୱାଇ-ଫାଇ କଲିଙ୍ଗ" + + + + + "ଅଫ୍" + "ପସନ୍ଦ କରାଯାଇଥିବା ୱାଇ-ଫାଇ" + + + "କେବଳ ୱାଇ-ଫାଇ" + "{0}: ଫରୱାର୍ଡ କରାଯାଇନାହିଁ" + "{0}: {1}" + "{0}: {1} {2} ସେକେଣ୍ଡ ପରେ" + "{0}: ଫରୱାର୍ଡ କରାଯାଇନାହିଁ" + "{0}: ଫର୍‌ୱର୍ଡ କରାଗଲା ନାହିଁ" + "ଫିଚର୍ କୋଡ୍ ସମ୍ପୂର୍ଣ୍ଣ।" + "ସଂଯୋଗରେ ସମସ୍ୟା କିମ୍ୱା ଅମାନ୍ୟ ଫିଚର୍ କୋଡ୍।" + "ଠିକ୍‍ ଅଛି" + "ନେଟ୍‍ୱର୍କରେ ଏକ ତ୍ରୁଟି ଥିଲା।" + "URL ମିଳିଲା ନାହିଁ।" + "ସାଇଟ୍‍ ପ୍ରମାଣୀକରଣ ସ୍କିମ୍‍ ସପୋର୍ଟ କରୁନାହିଁ।" + "ସ୍ୱୀକୃତି ଦେଇପାରିଲା ନାହିଁ।" + "ପ୍ରୋକ୍ସୀ ସର୍ଭର୍ ଦ୍ୱାରା ସ୍ୱୀକୃତି ଦିଆଯାଇପାରିଲା ନାହିଁ।" + "ସର୍ଭର୍‌ ସହ ସଂଯୋଗ କରିପାରିଲା ନାହିଁ।" + "ସର୍ଭର୍‍ ସହ ଯୋଗାଯୋଗ କରିପାରିଲା ନାହିଁ। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ସର୍ଭରକୁ ସଂଯୋଗର ସମୟ ସୀମା ସମାପ୍ତ ହୋଇଯାଇଛି।" + "ଏହି ପୃଷ୍ଠାରେ ବହୁତ ସର୍ଭର୍ ରିଡାଇରେକ୍ଟ ରହିଛି।" + "ପ୍ରୋଟୋକଲ୍‍ ସପୋର୍ଟ କରୁନାହିଁ।" + "ଏକ ନିରାପଦ ସଂଯୋଗ ସ୍ଥାପନ କରିପାରିଲା ନାହିଁ।" + "URL ବୈଧ ନଥିବାରୁ ପୃଷ୍ଠାଟି ଖୋଲିପାରିଲା ନାହିଁ।" + "ଫାଇଲ୍‍ ଆକ୍ସେସ୍‌ କରିପାରିଲା ନାହିଁ।" + "ଅନୁରୋଧ କରାଯାଇଥିବା ଫାଇଲ୍‍ ପାଇଲା ନାହିଁ।" + "ଅନେକ ଅନୁରୋଧ ଉପରେ କାମ ଚାଲୁଛି। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "%1$sରେ ସାଇନ୍‍-ଇନ୍‍ ତ୍ରୁଟି" + "ସିଙ୍କ୍" + "ସିଙ୍କ୍" + "ଅତ୍ୟଧିକ %s ଡିଲିଟ୍‍ କରିଦିଏ।" + "ଟାବଲେଟ୍‍ ଷ୍ଟୋରେଜ୍‍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ଥାନ ଖାଲି କରିବା ପାଇଁ କିଛି ଫାଇଲ୍‍ ଡିଲିଟ୍‍ କରନ୍ତୁ।" + "ୱାଚ୍‍ ଷ୍ଟୋରେଜ୍‍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ଥାନ ଖାଲି କରିବାକୁ କିଛି ଫାଇଲ୍‍ ଡିଲିଟ୍‍ କରନ୍ତୁ।" + "ଟିଭିର ଷ୍ଟୋରେଜ୍‍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ପେସ୍‍ ଖାଲି କରିବାକୁ କିଛି ଫାଇଲ୍‍ ଡିଲିଟ୍ କରନ୍ତୁ।" + "ଫୋନ୍‍ ଷ୍ଟୋରେଜ୍‍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ଥାନ ଖାଲି କରିବା ପାଇଁ କିଛି ଫାଇଲ୍‍ ଡିଲିଟ୍‍ କରନ୍ତୁ।" + + "ଅଜଣା ତୃତୀୟ ପକ୍ଷ ଅନୁଯାୟୀ" + + + "%s ଅନୁଯାୟୀ" + "ୱାର୍କ ପ୍ରୋଫାଇଲ୍‍ ଡିଲିଟ୍ ହେଲା" + + + + + + + + + + + + + "ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ବର୍ତ୍ତମାନ ଲିଭାଯିବ" + + + + + "ମୁଁ" + "ଟାବଲେଟ୍‌ର ବିକଳ୍ପ" + "ଟିଭି ବିକଳ୍ପଗୁଡ଼ିକ" + "ଫୋନ୍‌ ବିକଳ୍ପ" + "ସାଇଲେଣ୍ଟ ମୋଡ୍" + "ୱେୟାରଲେସ୍‌କୁ ଚାଲୁ କରନ୍ତୁ" + "ୱେୟାରଲେସ୍‌କୁ ବନ୍ଦ କରନ୍ତୁ" + "ସ୍କ୍ରୀନ୍‌ ଲକ୍‌" + "ପାୱାର୍ ଅଫ୍" + "ରିଙ୍ଗର୍‍ ଅଫ୍‍ ଅଛି" + "ରିଙ୍ଗର୍‍ କମ୍ପନ" + "ରିଙ୍ଗର୍‍ ଅନ୍‍ ଅଛି" + "Android ସିଷ୍ଟମ୍‍ ଅପଡେଟ୍‍" + "ଅପଡେଟ୍‍ କରିବାକୁ ପ୍ରସ୍ତୁତ କରାଯାଉଛି…" + "ଅପଡେଟ୍‍ ପ୍ୟାକେଜ୍‍ ପ୍ରୋସେସ୍‍ କରାଯାଉଛି…" + "ରିଷ୍ଟାର୍ଟ କରାଯାଉଛି…" + "ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍‌" + "ରିଷ୍ଟାର୍ଟ କରାଯାଉଛି…" + "ବନ୍ଦ କରାଯାଉଛି…" + "ଆପଣଙ୍କ ଟାବଲେଟ୍ ବନ୍ଦ ହୋଇଯିବ।" + "ଆପଣଙ୍କ ଟିଭି ବନ୍ଦ ହେବ।" + "ଆପଣଙ୍କ ଘଣ୍ଟା ବନ୍ଦ ହୋଇଯିବ।" + "ଆପଣଙ୍କ ଫୋନ୍ ବନ୍ଦ ହୋଇଯିବ।" + "ଆପଣ ବନ୍ଦ କରିବାକୁ ଚାହାନ୍ତି?" + "ନିରାପଦ ମୋଡ୍‍ରେ ରିବୁଟ୍‍ କରନ୍ତୁ" + "ଆପଣ ନିରାପଦ ମୋଡ୍‍ରେ ରିବୁଟ୍‍ କରିବେ କି? ଆପଣ ଇନଷ୍ଟଲ୍‍ କରିଥିବା ସମସ୍ତ ତୃତୀୟ ପକ୍ଷ ଆପ୍ଲିକେଶନ୍‌, ଏହାଦ୍ୱାରା ଅକ୍ଷମ ହୋଇଯିବ। ଆପଣ ପୁଣି ରିବୁଟ୍‍ କରିବା ପରେ ସେଗୁଡ଼ିକ ରିଷ୍ଟୋର୍‍ ହେବ।" + "କିଛି ସମୟ ପୂର୍ବରୁ" + "କୌଣସି ସମ୍ପ୍ରତି ଆପ୍‌ ନାହିଁ।" + "ଟାବଲେଟ ବିକଳ୍ପ" + "ଟିଭିର ବିକଳ୍ପ" + "ଫୋନ ବିକଳ୍ପ" + "ସ୍କ୍ରୀନ୍‌ ଲକ୍‌" + "ପାୱାର୍ ଅଫ୍" + "ଜରୁରୀକାଳୀନ" + "ବଗ୍‌ ରିପୋର୍ଟ" + + + + + "ବଗ୍ ରିପୋର୍ଟ ନିଅନ୍ତୁ" + "ଇ-ମେଲ୍ ମେସେଜ୍‍ ଭାବରେ ପଠାଇବାକୁ, ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଡିଭାଇସ୍‌ ବିଷୟରେ ଏହା ସୂଚନା ସଂଗ୍ରହ କରିବ। ବଗ୍ ରିପୋର୍ଟ ଆରମ୍ଭ ହେବାପରଠାରୁ ଏହାକୁ ପଠାଇବା ପାଇଁ କିଛି ସମୟ ଲାଗିବ, ଦୟାକରି ଧୈର୍ଯ୍ୟ ରଖନ୍ତୁ।" + "ଇଣ୍ଟରାକ୍ଟିଭ୍‍ ରିପୋର୍ଟ" + "ଅଧିକାଂଶ କ୍ଷେତ୍ରରେ ଏହା ବ୍ୟବହାର କରନ୍ତୁ। ଏହାଦ୍ୱାରା ଆପଣ ରିପୋର୍ଟର ପ୍ରଗତିକୁ ଟ୍ରାକ୍‍ କରିପାରିବେ, ସମସ୍ୟା ଉପରେ ଅଧିକ ବିବରଣୀ ଲେଖିପାରିବେ ଏବଂ ସ୍କ୍ରୀନଶଟ୍‍ ନେଇପାରିବେ। ଏହା କିଛି କମ୍‌-ବ୍ୟବହାର କରାଯାଇଥିବା ବିଭାଗକୁ ଛାଡ଼ିଦେଇପାରେ, ଯାହା ରିପୋର୍ଟ କରିବାକୁ ଅଧିକ ସମୟ ନିଏ।" + "ପୂର୍ଣ୍ଣ ରିପୋର୍ଟ" + "ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ପ୍ରତିକ୍ରିୟା ଦେଉନଥିବାବେଳେ କିମ୍ବା ବହୁତ ଧୀରେ ଚାଲୁଥିବାବେଳେ କିମ୍ବା ଆପଣ ସମସ୍ତ ରିପୋର୍ଟ ବିଭାଗ ଆବଶ୍ୟକ କରିବାବେଳେ ସିଷ୍ଟମର କମ୍‍ ହସ୍ତକ୍ଷେପ ପାଇଁ ଏହି ବିକଳ୍ପ ବ୍ୟବହାର କରନ୍ତୁ। ଅଧିକ ବିବରଣୀ ଲେଖିବାକୁ କିମ୍ବା ଅତିରିକ୍ତ ସ୍କ୍ରୀନଶଟ୍‍ ନେବାକୁ ଅନୁମତି ଦିଏନାହିଁ।" + + %d ସେକେଣ୍ଡରେ ବଗ୍‍ ରିପୋର୍ଟ ପାଇଁ ସ୍କ୍ରୀନଶଟ୍‍ ନେଉଛି। + %d ସେକେଣ୍ଡରେ ବଗ୍‍ ରିପୋର୍ଟ ପାଇଁ ସ୍କ୍ରୀନଶଟ୍‍ ନେଉଛି। + + "ସାଇଲେଣ୍ଟ ମୋଡ୍" + "ସାଉଣ୍ଡ ଅଫ୍ ଅଛି" + "ସାଉଣ୍ଡ ଅନ୍ ଅଛି" + "ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍" + "ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍ ଅନ୍ ଅଛି" + "ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍ ଅଫ୍ ଅଛି" + + + + + + + "ସେଟିଙ୍ଗ" + "ସହାୟକ" + "ଭଏସ୍‌ ସହାୟକ" + + + "999+" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "ସୁରକ୍ଷିତ ମୋଡ୍" + "Android ସିଷ୍ଟମ୍‌" + + + + + "ଯୋଗାଯୋଗ" + "ଆପଣଙ୍କ ଯୋଗାଯୋଗ ଆକ୍ସେସ୍ କରେ" + + + "ଲୋକେଶନ୍‌" + "ଏହି ଡିଭାଇସ୍‌ର ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍‍ କରେ" + + + "କ୍ୟାଲେଣ୍ଡର୍" + "ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର୍‍ ଆକ୍ସେସ୍‍ କରେ" + + + "SMS" + "SMS ମେସେଜ୍‍ ପଠାନ୍ତୁ ଓ ଦେଖନ୍ତୁ" + + + "ଷ୍ଟୋରେଜ୍‌" + "ଆପଣଙ୍କ ଡିଭାଇସ୍‌ରେ ଥିବା ଫଟୋ, ମିଡିଆ ଓ ଫାଇଲ୍‍ ଆକ୍ସେସ୍‍ କରେ" + + + "ମାଇକ୍ରୋଫୋନ୍" + "ଅଡିଓ ରେକର୍ଡ କରେ" + + + "କ୍ୟାମେରା" + "ଫଟୋ ନିଏ ଓ ଭିଡିଓ ରେକର୍ଡ କରେ" + + + "ଫୋନ୍‍" + "ଫୋନ୍‍ କଲ୍‍ କରେ ଏବଂ ପରିଚାଳନା କରେ" + + + "ବଡୀ ସେନ୍ସର୍" + "ଆପଣଙ୍କ ଗୁରୁତପୂର୍ଣ୍ଣ ସଂକେତଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର୍‍ ଡାଟା ଆକ୍ସେସ୍‍ କରେ" + + + "ୱିଣ୍ଡୋ କଣ୍ଟେଣ୍ଟ ହାସଲ କରନ୍ତୁ" + "ଆପଣ କାମ କରୁଥିବା ୱିଣ୍ଡୋର କଣ୍ଟେଣ୍ଟକୁ ଯାଞ୍ଚ କରନ୍ତୁ।" + "ସ୍ପର୍ଶ ଦ୍ୱାରା ଏକ୍ସପ୍ଲୋର୍‍ ଅନ୍‍ କରନ୍ତୁ" + "ଟାପ୍‍ କରାଯାଇଥିବା ଆଇଟମ୍‌ଗୁଡ଼ିକୁ ବଡ଼ ପାଟିରେ କୁହାଯିବ ଏବଂ ଜେଶ୍ଚର୍‍ ବ୍ୟବହାର କରି ସ୍କ୍ରୀନ୍‍ ଏକ୍ସପ୍ଲୋର୍‍ କରାଯିବ।" + "ଆପଣ ଟାଇପ୍‍ କରିବା ଟେକ୍ସଟ୍‍କୁ ଧ୍ୟାନଦେଇ ଦେଖନ୍ତୁ" + "ବ୍ୟକ୍ତିଗତ ଡାଟା ଅନ୍ତର୍ଭୁକ୍ତ ଅଛି, କ୍ରେଡିଟ୍‍ କାର୍ଡ ନମ୍ବର ଓ ପାସ୍‍ୱର୍ଡ।" + "ଡିସପ୍ଲେ ମେଗ୍ନିଫିକେଶନ୍‍ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ" + "ଡିସପ୍ଲେର ଜୁମ୍‍ ସ୍ତର ଓ ପୋଜିସନିଙ୍ଗ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।" + "ଜେଶ୍ଚର୍‍ କରନ୍ତୁ" + "ଟାପ୍‍, ସ୍ୱାଇପ୍‍, ପିଞ୍ଚ ଓ ଅନ୍ୟାନ୍ୟ ଜେଶ୍ଚର୍‍ ସମ୍ପାଦନ କରିପାରିବ।" + + + + + "ଷ୍ଟାଟସ୍‌ ବାର୍‌କୁ ଅକ୍ଷମ କିମ୍ୱା ସଂଶୋଧନ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ସ୍ଥିତି ବାର୍‍ ଅକ୍ଷମ କରିବାକୁ କିମ୍ବା ସିଷ୍ଟମ୍‍ ଆଇକନ୍‍ ଯୋଡ଼ିବା କିମ୍ବା ବାହାର କରିବାକୁ ଦେଇଥାଏ।" + "ଷ୍ଟାଟସ୍‍ ବାର୍‍ ରହିବାକୁ ଦିଅନ୍ତୁ" + "ଆପ୍‍ଟିକୁ ସ୍ଥିତି ବାର୍‍ ହେବାକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ସ୍ଥିତି ବାର୍‌କୁ ବଡ଼/ଛୋଟ କରନ୍ତୁ" + "ଷ୍ଟାଟସ୍‌ ବାର୍‍କୁ ବଡ଼ କିମ୍ବା ଛୋଟ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଶର୍ଟକଟ୍‍ ଇନଷ୍ଟଲ୍‍ କରନ୍ତୁ" + "ୟୁଜର୍‌ଙ୍କ ବିନା ବାଧାରେ ହୋମ୍‍ସ୍କ୍ରୀନ୍‍ ସର୍ଟକଟ୍‍ ଯୋଡ଼ିବାକୁ ଏକ ଆପ୍ଲିକେଶନ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଶର୍ଟକଟ୍‍ ଅନଇନଷ୍ଟଲ୍‍ କରନ୍ତୁ" + "ୟୁଜର୍‌ଙ୍କ ବିନା ବାଧାରେ ହୋମ୍‍ସ୍କ୍ରୀନ୍‍‍ର ଶର୍ଟକଟ୍‍ ବାହାର କରିବା ପାଇଁ ଗୋଟିଏ ଆପ୍ଲିକେଶନ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଆଉଟ୍‍ଗୋଇଙ୍ଗ କଲ୍‍‍କୁ ଅନ୍ୟ ରୁଟ୍‍ ଦିଅନ୍ତୁ" + "ଭିନ୍ନ ନମ୍ବରକୁ କଲ୍‍ ରିଡାଇରେକ୍ଟ କରିବା କିମ୍ବା ଏକାଠି କଲ୍‍ ଖାରଜ କରିବା ବିକଳ୍ପ ସହ ଆଉଟ୍‍ଗୋଇଙ୍ଗ କଲ୍‍ କରିବାବେଳେ ଡାୟଲ୍‍ କରାଯାଉଥିବା ନମ୍ବର ଦେଖିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + + + + + "ଟେକ୍ସଟ୍‍ ମେସେଜ୍‌ (SMS) ପ୍ରାପ୍ତ କରନ୍ତୁ" + "SMS ମେସେଜ୍‌ ପ୍ରାପ୍ତ କରିବାକୁ ତଥା ପ୍ରକ୍ରିୟା କରାଇବାକୁ ଆପ୍‍ଟିକୁ ଅନୁମତି ଦିଏ। ଏହାର ଅର୍ଥ ହେଉଛି, ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ପଠାଯାଇଥିବା ମେସେଜ୍‍ ଆପଣଙ୍କୁ ନଦେଖାଇ ଆପ୍‍ଟି ମନିଟର୍‍ କିମ୍ବା ଡିଲିଟ୍‍ କରିପାରେ।" + "ଟେକ୍ସଟ୍‍ ମେସେଜ୍‍ (MMS) ପ୍ରାପ୍ତ କରନ୍ତୁ" + "MMS ମେସେଜ୍‌ ପ୍ରାପ୍ତ କରିବାକୁ ତଥା ପ୍ରକ୍ରିୟା କରାଇବାକୁ ଆପ୍‍ଟିକୁ ଅନୁମତି ଦିଏ। ଏହାର ଅର୍ଥ, ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ପଠାଯାଇଥିବା ମେସେଜ୍‍ ଆପଣଙ୍କୁ ନଦେଖାଇ ଆପ୍‍ଟି ମନିଟର୍‍ କିମ୍ବା ଡିଲିଟ୍‍ କରିପାରେ।" + "ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ମେସେଜ୍‍ ପଢ଼ନ୍ତୁ" + "ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ପ୍ରାପ୍ତ ହୋଇଥିବା ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ମେସେଜ୍‍ ପଢିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଜରୁରୀକାଳୀନ ଅବସ୍ଥା ବିଷୟରେ ଆପଣଙ୍କୁ ସତର୍କ କରାଇବାକୁ କିଛି ଲୋକେଶନ୍‍ରେ ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ସତର୍କ ଡେଲିଭର୍ କରାଯାଇଥାଏ। ଏକ ଜରୁରୀକାଳୀନ ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ପ୍ରାପ୍ତ ହେବାପରେ ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ ଡିଭାଇସ୍‍ର କାର୍ଯ୍ୟକ୍ଷମତା କିମ୍ବା ସଞ୍ଚାଳନାରେ ବାଧା ପହଞ୍ଚାଇପାରନ୍ତି।" + "ସବସ୍କ୍ରାଇବ୍ ହୋଇଥିବା ଫୀଡ୍‌କୁ ପଢ଼ନ୍ତୁ" + "ଆପ୍‍କୁ, କିଛି ସମୟ ପୂର୍ବରୁ ସିଙ୍କ ହୋଇଥିବା ଫୀଡ୍‍ ବିଷୟରେ ବିବରଣୀ ପ୍ରାପ୍ତ କରିବାକୁ ଦେଇଥାଏ।" + "SMS ମେସେଜ୍‍ ପଠାନ୍ତୁ ଓ ଦେଖନ୍ତୁ" + "ଆପ୍‌କୁ SMS ମେସେଜ୍ ପଠେଇବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅପ୍ରତ୍ୟାଶିତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ। ହାନୀକାରକ ଆପ୍‌ ଆପଣଙ୍କ ବିନା ସ୍ୱୀକୃତିରେ ମେସେଜ୍‍ ପଠାଇ, ଆପଣଙ୍କ ପଇସା ଖର୍ଚ୍ଚ କରାଇପାରେ।" + "ଆପଣଙ୍କ ଟେକ୍ସଟ୍‍ ମେସେଜ୍‍ (SMS କିମ୍ବା MMS) ପଢ଼ନ୍ତୁ" + + + + + "ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ସମସ୍ତ SMS (ଟେକ୍ସଟ୍‍) ମେସେଜ୍‍ ଏହି ଆପ୍‍ ପଢ଼ିପାରେ।" + "ଟେକ୍ସଟ୍‍ ମେସେଜ୍‍ (WAP) ପ୍ରାପ୍ତ କରନ୍ତୁ" + "ଆପ୍‌କୁ WAP ମେସେଜିଙ୍ଗକୁ ପ୍ରାପ୍ତ ଓ ବିକାଶ କରିବାକୁ ଦେଇଥାଏ। ଏହି ଅନୁମତିରେ ଆପଣ ସେମାନଙ୍କୁ ଦେଖାଯାଇଥିବା ମେସେଜ୍ ଉପରେ ନଜର ରଖିବା ଏବଂ ଡିଲିଟ୍‍ କରିବାର କ୍ଷମତା ସାମିଲ୍ ଅଛି।" + "ଚାଲୁଥିବା ଆପ୍‌ଗୁଡ଼ିକୁ ପୁନଃପ୍ରାପ୍ତ କରନ୍ତୁ" + "ବର୍ତ୍ତମାନ ତଥା ନିକଟରେ ଚାଲୁଥିବା କାର୍ଯ୍ୟ ବିଷୟରେ ସୂଚନାକୁ ହାସଲ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଆପଟି ଡିଭାଇସ୍‌ର କେଉଁ ଆପ୍ଲିକେଶନ୍‍ରେ ଉପଯୋଗ କରାଯାଇଥିଲା, ତାହାର ସୂଚନାକୁ ଜାଣିପାରେ।" + "ପ୍ରୋଫାଇଲ୍‍ ଓ ଡିଭାଇସ୍‍ ମାଲିକଙ୍କୁ ପରିଚାଳନା କରେ" + "ପ୍ରୋଫାଇଲ୍‍ ମାଲିକ ଓ ଡିଭାଇସ୍‍ ମାଲିକଙ୍କ ପ୍ରୋଫାଇଲ୍‍ ସେଟ୍‍ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଚାଲୁଥିବା ଆପ୍‌ଗୁଡ଼ିକର କ୍ରମକୁ ପୁଣି ସଜାନ୍ତୁ" + "ଆପ୍‍ଟି, ଆପଣ କରୁଥିବା କାମକୁ ଫୋର୍‍ଗ୍ରାଉଣ୍ଡ ତଥା ବ୍ୟାକ୍‍ଗ୍ରାଉଣ୍ଡକୁ ନେଇପାରିବ। ଆପଣଙ୍କ ବିନା ଅନୁମତିରେ ଆପ୍‍ଟି ଏହା କରିପାରିବ।" + "କାର୍ ମୋଡ୍‌କୁ ସକ୍ଷମ କରନ୍ତୁ" + "କାର୍‍ ମୋଡ୍‍ ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଅନ୍ୟ ଆପ୍‍ ବନ୍ଦ କରନ୍ତୁ" + "ଆପ୍‌କୁ ଅନ୍ୟ ଆପ୍‌ର ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଟ ପ୍ରକ୍ରିୟାକୁ ସମାପ୍ତ କରିବାକୁ ଦେଇଥାଏ। ଏହି କାରଣରୁ ଅନ୍ୟ ଆପ୍‌ଗୁଡ଼ିକ ଚାଲିବା ବନ୍ଦ ହୋଇଯାଇପାରେ।" + + + + + + + + + + + + + "ଆପ୍‍କୁ, ସର୍ବଦା ଚାଲୁଥିବା କରନ୍ତୁ" + "ଆପ୍‍ଟି ନିଜକୁ ମେମୋରୀରେ ଭାଗ କରିବାକୁ ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅନ୍ୟ ଆପ୍‍ଗୁଡ଼ିକ ପାଇଁ ମେମୋରୀ ଉପଲବ୍ଧକୁ କମ୍‌ କରିବା ସହ ଟାବ୍‍ଲେଟ୍‍ଟିକୁ ମନ୍ଥର କରିବ।" + "ମେମୋରୀରେ ଆପ୍‍ଟି ନିଜକୁ ଭାଗ କରିବାକୁ ଦେଇଥାଏ। ଏହା ଟିଭିକୁ ଧୀର କରି ଅନ୍ୟ ଆପ୍‍ ପାଇଁ ଉପଲବ୍ଧ ମେମୋରୀକୁ ସୀମିତ କରିପାରେ।" + "ଆପ୍‍ଟି ନିଜକୁ ମେମୋରୀରେ ଭାଗ କରିବାକୁ ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅନ୍ୟ ଆପ୍‍ଗୁଡ଼ିକ ପାଇଁ ମେମୋରୀ ଉପଲବ୍ଧକୁ କମ୍‌ କରିବା ସହ ଫୋନ୍‍ଟିକୁ ମନ୍ଥର କରିବ।" + + + + + "ଆପ୍‍ ଷ୍ଟୋରେଜ୍‍ ସ୍ଥାନର ମାପ କରନ୍ତୁ" + "ଆପ୍‍ର କୋଡ୍‍, ଡାଟା ଓ କ୍ୟାଶ୍‌ ଆକାର ହାସଲ କରିବା ପାଇଁ ଏହାକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ସିଷ୍ଟମ୍‍ ସେଟିଙ୍ଗ ବଦଳାନ୍ତୁ" + "ଆପ୍‍କୁ, ସିଷ୍ଟମର ସେଟିଙ୍ଗ ଡାଟା ବଦଳାଇବାକୁ ଦେଇଥାଏ। ହାନୀକାରକ ଆପ୍‍ ଦ୍ୱାରା ଆପଣଙ୍କ ସିଷ୍ଟମର କନଫିଗରେସନ୍‍ ଖରାପ ହୋଇପାରେ।" + "ଆରମ୍ଭ ହେଲେ ଚଲାନ୍ତୁ" + "ସିଷ୍ଟମ୍‍ ବୁଟ୍ ଶେଷ ହେବା କ୍ଷଣି ଆପ୍‍ଟିକୁ ସ୍ୱତଃ ଆରମ୍ଭ ହେବାକୁ ଦେଇଥାଏ। ଏହା କାରଣରୁ ଟାବଲେଟଟ୍‌ଟି ଚାଲୁ ହେବାରେ ଅଧିକ ସମୟ ଲାଗିପାରେ ଏବଂ ଆପ୍‌ଟି ଲଗାତାର ଚାଲିବା ଦ୍ୱାରା ସମଗ୍ର ଟାବଲେଟଟ୍‌ ମନ୍ଥର ହୋଇଯାଇପାରେ।" + "ବୁଟିଙ୍ଗ ସମାପ୍ତ ହେବପରେ ଏହି ଆପ୍‌କୁ ଆରମ୍ଭ ହେବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା TV ଚଳାଇବାକୁ ଅଧିକ ସମୟ ନେଇପାରେ ଏବଂ ଆପ୍‌କୁ ସବୁବେଳେ ଚାଲିବାରେ ସମଗ୍ର ଟାବଲେଟ୍ ଧୀର କରିପାରେ।" + "ସିଷ୍ଟମ୍‍ ବୁଟ୍ ଶେଷ ହେବା କ୍ଷଣି ଆପ୍‍ଟିକୁ ସ୍ୱତଃ ଆରମ୍ଭ ହେବାକୁ ଦେଇଥାଏ। ଏହା କାରଣରୁ ଫୋନ୍‍ଟି ଚାଲୁ ହେବାରେ ଅଧିକ ସମୟ ଲାଗିପାରେ ଏବଂ ଆପ୍‌ଟି ଲଗାତାର ଚାଲିବା ଦ୍ୱାରା ସମଗ୍ର ଫୋନ୍‌ ମନ୍ଥର ହୋଇଯାଇପାରେ।" + "ଷ୍ଟିକୀ ବ୍ରୋଡକାଷ୍ଟ ପଠାନ୍ତୁ" + "ଆପ୍‌କୁ ଷ୍ଟିକୀ ବ୍ରଡ୍‍କାଷ୍ଟ ପଠାଇବାକୁ ଅନୁମତି ଦେଇଥାଏ, ଯାହା ବ୍ରଡ୍‍କାଷ୍ଟ ସମାପ୍ତ ହେବାପରେ ବି ରହିଥାଏ। ଅତ୍ୟଧିକ ଉପଯୋଗ ଦ୍ୱାରା ଟାବଲେଟ୍‍ ମନ୍ଥର ହୋଇପାରେ କିମ୍ବା ଅଧିକ ମେମୋରୀର ବ୍ୟବହାର କରିବା କାରଣରୁ ଏହା ଅସ୍ଥିର ହୋଇପାରେ।" + "ଷ୍ଟିକୀ ବ୍ରଡ୍‌କାଷ୍ଟ ପଠାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ, ଯାହା ବ୍ରଡ୍‌କାଷ୍ଟ ଶେଷ ହେବାପରେ ରହିଥାଏ। ଅତିରିକ୍ତ ବ୍ୟବହାର ଦ୍ୱାରା ଅଧିକ ମେମୋରୀ ବ୍ୟବହାର ହୋଇ ଟିଭିକୁ ଧୀର କିମ୍ବା ଅସ୍ଥିର କରିପାରେ।" + "ଷ୍ଟିକୀ ବ୍ରଡ୍‌କାଷ୍ଟ ପଠାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ, ଯାହା ବ୍ରଡ୍‌କାଷ୍ଟ ଶେଷ ହେବାପରେ ରହିଥାଏ। ଅତିରିକ୍ତ ବ୍ୟବହାର ଦ୍ୱାରା ଅଧିକ ମେମୋରୀ ବ୍ୟବହାର ହୋଇ ଫୋନ୍‌କୁ ମନ୍ଥର କିମ୍ବା ଅସ୍ଥିର କରିପାରେ।" + "ଆପଣଙ୍କ ଯୋଗାଯୋଗ ପଢ଼ନ୍ତୁ" + "ଜଣେ ନିର୍ଦ୍ଦିଷ୍ଟ ବ୍ୟକ୍ତିଙ୍କ ସହ ଆପଣ କେତେଥର କଲ୍‍, ଇମେଲ୍‍, ତଥା ଯୋଗାଯୋଗ କରିଛନ୍ତି, ତାହାର ନିୟମିତତା ସମେତ ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ ଷ୍ଟୋର୍ ହୋଇଥିବା ଯୋଗାଯୋଗ ବିଷୟରେ ଡାଟା ପଢ଼ିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହ ଅନୁମତି ଦ୍ୱାରା ଆପଣଙ୍କ କଲ୍‍ ଲଗ୍ ସେଭ୍‍ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ ତଥା ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ ଅଜ୍ଞାତରେ କଲ୍‍ ଲଗ୍‍ ଡାଟା ଶେୟାର କରିପାରନ୍ତି।" + "ଜଣେ ନିର୍ଦ୍ଦିଷ୍ଟ ବ୍ୟକ୍ତିଙ୍କ ସହ ଆପଣ କେତେଥର କଲ୍‍, ଇମେଲ୍‍, ତଥା ଯୋଗାଯୋଗ କରିଛନ୍ତି, ତାହାର ନିୟମିତତା ସମେତ ଆପଣଙ୍କ ଟିଭିରେ ଷ୍ଟୋର୍ ହୋଇଥିବା ଯୋଗାଯୋଗ ବିଷୟରେ ଡାଟା ପଢ଼ିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହାର ଅନୁମତି ଦ୍ୱାରା ଆଙ୍ଙକକ କଲ୍‍ ଲଗ୍ ସେଭ୍‍ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ ତଥା ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ ଅଜ୍ଞାତରେ କଲ୍‍ ଲଗ୍‍ ଡାଟା ଶେୟାର କରିପାରନ୍ତି।" + "ଜଣେ ନିର୍ଦ୍ଦିଷ୍ଟ ବ୍ୟକ୍ତିଙ୍କ ସହ ଆପଣ କେତେ ବ୍ୟବଧାନରେ କଲ୍‌, ଇମେଲ ତଥା ଯୋଗାଯୋଗ କରିଛନ୍ତି, ସେସବୁ ଅନ୍ତର୍ଭୁକ୍ତ କରି ଆପଣଙ୍କ ଫୋନ୍‍ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ଯୋଗାଯୋଗ ବିଷୟରେ ଡାଟା ପଢିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହି ଅନୁମତି ଦ୍ୱାରା ଆପ୍‍ଟି ଆପଣଙ୍କ ଯୋଗାଯୋଗ ଡାଟା ସେଭ୍‍ କରିପାରିବ ଏବଂ ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ ବିନା ଜ୍ଞାତସାରରେ ଯୋଗାଯୋଗ ଡାଟା ଶେୟାର୍‍ କରିପାରନ୍ତି।" + "ନିଜ ଯୋଗାଯୋଗ ସଂଶୋଧନ କରନ୍ତୁ" + "ଜଣେ ନିର୍ଦ୍ଦିଷ୍ଟ ଯୋଗାଯୋଗଙ୍କ ସହ ଆପଣ କେତେ ବ୍ୟବଧାନରେ କଲ୍‌, ଇମେଲ ତଥା ଯୋଗାଯୋଗ କରିଛନ୍ତି, ସେସବୁ ଅନ୍ତର୍ଭୁକ୍ତ କରି ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ଯୋଗାଯୋଗ ବିଷୟକ ଡାଟା ବଦଳାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହି ଅନୁମତି ଦ୍ୱାରା ଆପ୍‍ଟି ଯୋଗାଯୋଗ ଡାଟା ଡିଲିଟ୍‍ କରିପାରେ।" + "ଜଣେ ନିର୍ଦ୍ଦିଷ୍ଟ ଯୋଗାଯୋଗଙ୍କ ସହ ଆପଣ କେତେ ବ୍ୟବଧାନରେ କଲ୍‌, ଇମେଲ ତଥା ଯୋଗାଯୋଗ କରିଛନ୍ତି, ସେସବୁକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରି ଆପଣଙ୍କ ଟିଭିରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ଯୋଗାଯୋଗ ବିଷୟରେ ଡାଟା ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହି ଅନୁମତି ଦ୍ୱାରା ଆପ୍‍ଟି ଯୋଗାଯୋଗ ଡାଟା ଡିଲିଟ୍‌ କରିପାରେ।" + "ଜଣେ ନିର୍ଦ୍ଦିଷ୍ଟ ଯୋଗାଯୋଗଙ୍କ ସହ ଆପଣ କେତେ ବ୍ୟବଧାନରେ କଲ୍‌, ଇମେଲ ତଥା ଯୋଗାଯୋଗ କରିଛନ୍ତି, ସେସବୁ ଅନ୍ତର୍ଭୁକ୍ତ କରି ଆପଣଙ୍କ ଫୋନ୍‍ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ଯୋଗାଯୋଗ ବିଷୟରେ ଡାଟା ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହି ଅନୁମତି ଦ୍ୱାରା ଆପ୍‍ଟି ଯୋଗାଯୋଗ ଡାଟା ଡିଲିଟ୍‍ କରିପାରେ।" + "କଲ୍‌ ଲଗ୍‌ ପଢ଼ନ୍ତୁ" + "ଏହି ଆପ୍‍ ଆପଣଙ୍କ କଲ୍‍ ହିଷ୍ଟୋରୀ ପଢ଼ିପାରେ।" + "କଲ୍‍ ଲଗ୍‍ ଲେଖନ୍ତୁ" + "ଇନ୍‍କମିଙ୍ଗ ତଥା ଆଉଟ୍‍ଗୋଇଙ୍ଗ କଲ୍‌ ଡାଟା ସହ ଆପଣଙ୍କ ଟାବ୍‍ଲେଟ୍‍ର କଲ୍‍ ଲଗ୍‍ ବଦଳାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ କଲ୍‍ ଲଗ୍‍ ଲିଭାଇବାକୁ କିମ୍ବା ବଦଳାଇବାକୁ ଏହା ବ୍ୟବହାର କରିପାରନ୍ତି।" + "ଇନ୍‍କମିଙ୍ଗ ତଥା ଆଉଟ୍‍ଗୋଇଙ୍ଗ କଲ୍‌ ଡାଟା ସହ ଆପଣଙ୍କ ଟିଭିର କଲ୍‍ ଲଗ୍‍ ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ କଲ୍‍ ଲଗ୍‍ ଲିଭାଇବାକୁ କିମ୍ବା ବଦଳାଇବାକୁ ଏହାକୁ ବ୍ୟବହାର କରିପାରନ୍ତି।" + "ଇନ୍‍କମିଙ୍ଗ ତଥା ଆଉଟ୍‍ଗୋଇଙ୍ଗ କଲ୍‌ ଡାଟା ସହ ଆପଣଙ୍କ ଫୋନ୍‍ର କଲ୍‍ ଲଗ୍‍ ବଦଳାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ କଲ୍‍ ଲଗ୍‍ ଲିଭାଇବାକୁ କିମ୍ବା ବଦଳାଇବାକୁ ଏହା ବ୍ୟବହାର କରିପାରନ୍ତି।" + "ବଡୀ ସେନ୍ସର୍‍ ଆକ୍ସେସ୍‍ କରେ (ଯେପରିକି ହୃଦ୍‍ ହାର ମନିଟର୍‍)" + "ଆପ୍‌କୁ ସେନ୍ସର୍ ଡେଟା ପର୍ଯ୍ୟନ୍ତ ପହଞ୍ଚିବାକୁ ଦେଇଥାଏ, ଯାହା ଆପଣଙ୍କ ଶାରୀରିକ ସ୍ଥିତିର ନିରୀକ୍ଷଣ କରିଥାଏ, ଯେପରିକି ଆପଣଙ୍କ ହୃଦୟ ସ୍ତର।" + + + + + + + + + "କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟରେ ଯୋଡ଼ନ୍ତୁ କିମ୍ବା ସଂଶୋଧନ କରନ୍ତୁ ଏବଂ ମାଲିକଙ୍କ ଅଜାଣତରେ ଅତିଥିମାନଙ୍କୁ ଇମେଲ୍ ପଠାନ୍ତୁ।" + + + + + "ଏହି ଆପ୍‍, ଆପଣଙ୍କ ଫୋନ୍‌ରେ କ୍ୟାଲେଣ୍ଡର୍‌ ଇଭେଣ୍ଟଗୁଡ଼ିକୁ ଯୋଡ଼ିପାରେ, ବାହାର କରିପାରେ କିମ୍ବା ବଦଳାଇପାରେ। କ୍ୟାଲେଣ୍ଡର୍‌ ମାଲିକଙ୍କ ପାଖରୁ ଆସିଥିବା ପରି ଜଣା‍ପଡ଼ିବା ମେସେଜ୍‍କୁ ଏହି ଆପ୍‍ ପଠାଇପାରେ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟ ବଦଳାଇପାରେ।" + "ଅତିରିକ୍ତ ଲୋକେଶନ୍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡକୁ ଆକ୍ସେସ୍‍ କରନ୍ତୁ" + "ଅତିରିକ୍ତ ଲୋକେଶନ୍‍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡ ଆକ୍ସେସ୍‌ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। GPS କିମ୍ବା ଅନ୍ୟ ଲୋକେଶନ୍‍ ସୋର୍ସଗୁଡିକରେ ଆପ୍‍ଟି ପ୍ରଭାବ ପକାଇପାରେ।" + "ନିର୍ଦ୍ଦିଷ୍ଟ ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍ କରେ (GPS ଏବଂ ନେଟ୍‌ୱର୍କ-ଆଧାରିତ)" + "ଏହି ଆପ୍‍, GPS କିମ୍ୱା ନେଟ୍‌ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍‍ ଟାୱାର୍‍ ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ। ଏହି ଲୋକେଶନ୍‌ ସେବା, ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଅନ୍‍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍‌ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ। ଏହାଦ୍ୱାରା ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ ବଢ଼ିପାରେ।" + "ପାଖାପାଖି ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍‍ କରେ (ନେଟ୍‌ୱର୍କ-ଆଧାରିତ)" + "ଆପଣଙ୍କ ଅଡିଓ ସେଟିଙ୍ଗକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ" + "ଆପ୍‌କୁ ଗ୍ଲୋବାଲ୍ ଅଡିଓ ସେଟିଙ୍ଗ, ଯେପରିକି ଭଲ୍ୟୁମ୍‌କୁ ସଂଶୋଧିତ କରିବାକୁ ଏବଂ ଆଉଟପୁଟ୍ ପାଇଁ ସ୍ପିକର୍‌ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ" + + + "SIMକୁ କମାଣ୍ଡ ପଠାନ୍ତୁ" + "SIMକୁ କମାଣ୍ଡ ପଠାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ବହୁତ ବିପଦପୂର୍ଣ୍ଣ।" + "ଫଟୋ ଓ ଭିଡିଓଗୁଡ଼ିକୁ ନିଅନ୍ତୁ" + "ଏହି ଆପ୍‍ ଯେକୌଣସି ସମୟରେ କ୍ୟାମେରା ବ୍ୟବହାର କରି ଫଟୋ ଉଠାଇପାରେ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିପାରେ।" + "କମ୍ପନ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ଭାଇବ୍ରେଟର୍‍ ନିୟନ୍ତ୍ରଣ କରିବାକୁ ଦେଇଥାଏ।" + "ସିଧାସଳଖ ଫୋନ୍ ନମ୍ବରଗୁଡ଼ିକୁ କଲ୍ କରନ୍ତୁ" + "ଆପଣଙ୍କ ହସ୍ତକ୍ଷେପ ବିନା ଫୋନ୍‌ ନମ୍ଵରକୁ କଲ୍ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅପ୍ରତ୍ୟାଶିତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ କିମ୍ବା କଲ୍ ହୋଇପାରେ। ଧ୍ୟାନଦିଅନ୍ତୁ ଯେ, ଏହା ଆପ୍‌କୁ କୌଣସି ଜରୁରୀକାଳୀନ ନମ୍ବରରେ କଲ୍ କରିବାକୁ ଅନୁମତି ଦିଏନାହିଁ। ହାନୀକାରକ ଆପ୍‌ ଆପଣଙ୍କ ବିନା ସ୍ୱୀକୃତିରେ କଲ୍ କରି ଆପଣଙ୍କ ପଇସା ଖର୍ଚ୍ଚ କରାଇପାରେ।" + "IMS କଲ୍‍ ସେବା ଆକ୍ସେସ୍‍ କରେ" + "ଆପଣଙ୍କ ହସ୍ତକ୍ଷେପ ବିନା କଲ୍‍ କରିପାରିବା ପାଇଁ ଆପ୍‌କୁ IMS ସେବା ବ୍ୟବହାର କରିବାକୁ ଦିଏ।" + "ଫୋନ୍‍ ସ୍ଥିତି ଓ ପରିଚୟ ପଢ଼ନ୍ତୁ" + "ଆପ୍‌କୁ ଡିଭାଇସ୍‌ର ଫୋନ୍‌ ବୈଶିଷ୍ଟ୍ୟ ଆକ୍ସେସ୍‍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହି ଅନୁମତି ଆପ୍‌କୁ ଫୋନ୍‌ ନମ୍ବର୍ ଓ ଡିଭାଇସ୍‌ IDଗୁଡ଼ିକୁ ନିର୍ଦ୍ଧାରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ, କଲ୍ ସକ୍ରିୟ ଥିଲେ ବି ଏବଂ କଲ୍ ଦ୍ୱାରା ସଂଯୋଗ ଥିବା ରିମୋଟ୍‌ ନମ୍ବର୍‌କୁ ମଧ୍ୟ।" + + + + + + + + + + + + + "ଟାବଲେଟ୍‌କୁ ସ୍ଲୀପିଙ୍ଗ ମୋଡ୍‌କୁ ଯିବାକୁ ରୋକନ୍ତୁ" + "ଟିଭିକୁ ସ୍ଲୀପିଙ୍ଗ ମୋଡ୍‌ରୁ ଯିବାକୁ ରୋକନ୍ତୁ।" + "ଫୋନକୁ ସ୍ଲୀପିଙ୍ଗ ମୋଡ୍‌କୁ ଯିବାକୁ ରୋକନ୍ତୁ" + "ଆପ୍‍କୁ, ଟାବଲେଟ୍‍ଟିକୁ ସ୍ଲୀପ୍‍ ମୋଡ୍‍କୁ ଯିବାରେ ପ୍ରତିରୋଧ କରିବାକୁ ଦେଇଥାଏ।" + "TVକୁ ଶୁଆଇବାକୁ ଯିବାରୁ ରୋକିବାକୁ ଆପ୍‌ ଅନୁମତି ଦେଇଥାଏ।" + "ଆପ୍‍କୁ, ଫୋନ୍‌ଟିକୁ ସ୍ଲୀପ୍‍ ମୋଡ୍‍କୁ ଯିବାରେ ପ୍ରତିରୋଧ କରିବାକୁ ଦେଇଥାଏ।" + "ଇନଫ୍ରାରେଡ୍‍ ସଂଚାରିତ କରନ୍ତୁ" + "ଟାବଲେଟ୍‍ର ଇନଫ୍ରାରେଡ୍‍ ଟ୍ରାନ୍ସମିଟର୍‍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଟିଭିର ଇନ୍‍ଫ୍ରାରେଡ୍‍ ଟ୍ରାନ୍ସମିଟର୍‍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ।" + "ଫୋନ୍‍ର ଇନଫ୍ରାରେଡ୍‍ ଟ୍ରାନ୍ସମିଟର୍‍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ୱାଲପେପର୍‍ ସେଟ୍ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ସିଷ୍ଟମ୍‍ ୱାଲପେପର୍‍ ସେଟ୍‍ କରିବାକୁ ଦେଇଥାଏ।" + "ଆପଣଙ୍କ ୱାଲ୍‍ପେପର୍‍ର ଆକାର ଆଡ୍‌ଜଷ୍ଟ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ସିଷ୍ଟମ୍‍ ୱାଲପେପର୍‍ ଆକାରର ସୂଚନା ସେଟ୍‍ କରିବାକୁ ଦେଇଥାଏ।" + "ଟାଇମ୍ ଜୋନ୍‍ ସେଟ୍ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ଟାବଲେଟ୍‌ର ଟାଇମ୍‍ ଜୋନ୍‍ ବଦଳାଇବାକୁ ଦେଇଥାଏ।" + "ଟିଭିର ସଂଯୋଗ ପ୍ରବନ୍ଧିତ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ।" + "ଆପ୍‍କୁ, ଫୋନ୍‍ର ଟାଇମ୍‍ ଜୋନ୍‍ ବଦଳାଇବାକୁ ଦେଇଥାଏ।" + "ଡିଭାଇସ୍‍ରେ ଆକାଉଣ୍ଟ ଖୋଜନ୍ତୁ" + "ଟାବଲେଟ୍‌ ଦ୍ୱାରା ପରିଚିତ ଆକାଉଣ୍ଟର ତାଲିକା ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣ ଇନଷ୍ଟଲ୍‍ କରିଥିବା ଆପ୍ଲିକେଶନ୍‍ ଦ୍ୱାରା ତିଆରି କରାଯାଇଥିବା କୌଣସି ଆକାଉଣ୍ଟ ବି ଏଥିରେ ସାମିଲ୍ ହୋଇପାରେ।" + "TV ଦ୍ୱାରା ପରିଚିତ ଆକାଉଣ୍ଟର ତାଲିକା ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ। ଫଳରେ ଆପଣଙ୍କ ଦ୍ୱାରା ଇନଷ୍ଟଲ୍‍ କରାଯାଇଥିବା ଅନୁପ୍ରୟୋଗରେ ଖୋଲାଯାଇଥିବା କୌଣସି ବି ଆକାଉଣ୍ଟରେ ସାମିଲ୍ ହୋଇପାରିବ।" + "ଫୋନ୍‌ ଦ୍ୱାରା ପରିଚିତ ଆକାଉଣ୍ଟର ତାଲିକା ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣ ଇନଷ୍ଟଲ୍‍ କରିଥିବା ଆପ୍ଲିକେଶନ୍‍ ଦ୍ୱାରା ତିଆରି କରାଯାଇଥିବା କୌଣସି ଆକାଉଣ୍ଟ ବି ଏଥିରେ ସାମିଲ୍ ହୋଇପାରେ।" + "ନେଟ୍‍ୱର୍କ ସଂଯୋଗ ଦେଖନ୍ତୁ" + "କେଉଁ ନେଟ୍‌ୱର୍କ ଉପସ୍ଥିତ ତଥା ସଂଯୁକ୍ତ ଅଛି, ତାହା ବିଷୟରେ ସୂଚନା ଦେଖିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ପୂର୍ଣ୍ଣ ନେଟ୍‌ୱର୍କ ଆକ୍ସେସ୍‍ ରହିଛି" + "ଆପ୍‌କୁ ନେଟ୍‌ୱର୍କ ସକେଟ୍‌ ବନେଇବା ଏବଂ କଷ୍ଟମ୍ ନେଟ୍‌ୱର୍କ ପ୍ରୋଟୋକଲ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ବ୍ରାଉଜର୍ ଏବଂ ଅନ୍ୟ ଆପ୍ଲିକେଶନ୍‍ ଦ୍ୱାରା ଇଣ୍ଟରନେଟ୍‌କୁ ଡାଟା ପଠାଯାଇପାରେ, ତେଣୁ ଏହି ଅନୁମତି ଇଣ୍ଟରନେଟ୍‌କୁ ଡାଟା ପଠେଇବା ପାଇଁ ଆବଶ୍ୟକ ହୁଏନାହିଁ।" + "ନେଟୱର୍କ ସଂଯୋଗକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ" + "ନେଟୱର୍କ ସଂଯୋଗର ସ୍ଥିତି ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଟିଥ‍ର୍‌ ହୋଇଥିବା କନେକ୍ଟିଭିଟୀକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ" + "ଟିଥର୍ ହୋଇଥିବା ନେଟୱର୍କ ସଂଯୋଗର ସ୍ଥିତି ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ୱାଇ-ଫାଇ ସଂଯୋଗ ଦେଖନ୍ତୁ" + "ୱାଇ-ଫାଇ ନେଟୱର୍କିଙ୍ଗ ବିଷୟରେ ସୂଚନା ଦେଖିବାକୁ ଆପ୍‍‍କୁ ଅନୁମତି ଦିଏ, ଯେପରିକି ୱାଇ-ଫାଇ ସକ୍ଷମ ହୋଇଛି କି ନାହିଁ ଏବଂ ସଂଯୁକ୍ତ ହୋଇଥିବା ୱାଇ-ଫାଇ ଡିଭାଇସ୍‍ର ନାମ।" + "ୱାଇ-ଫାଇରୁ ସଂଯୋଗ ଓ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ" + "ଆପ୍‌କୁ ୱାଇ-ଫାଇ ଆକ୍ସେସ୍‍ ପଏଣ୍ଟ ସହିତ ସଂଯୋଗ ଓ ବିଚ୍ଛିନ୍ନ କରିବାକୁ ତଥା ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ପାଇଁ ଡିଭାଇସ୍‌ କନଫିଗରେଶନ୍‍ରେ ପରିବର୍ତ୍ତନ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ୱାଇ-ଫାଇ ମଲ୍ଟିକାଷ୍ଟ ରିସେପଶନ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ" + "କେବଳ ଆପଣଙ୍କ ଟାବ୍‍ଲେଟ୍‍ ନୁହେଁ, ବରଂ ମଲ୍ଟିକାଷ୍ଟ ଠିକଣାଗୁଡ଼ିକ ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍‍ୱର୍କରେ ଥିବା ସମସ୍ତ ଡିଭାଇସ୍‌‍କୁ ପଠାଯିବା ପ୍ୟାକେଟ୍‍ଗୁଡ଼ିକ ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍‍ଟି ଅନୁମତି ଦେଇଥାଏ। ଅଣ-ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍‍ ତୁଳନାରେ ଏହା ଅଧିକ ପାୱାର୍‍ ବ୍ୟବହାର କରେ।" + "କେବଳ ଆପଣଙ୍କ ଟିଭି ନୁହେଁ, ମଲ୍ଟିକାଷ୍ଟ ଠିକଣା ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କରେ ସମସ୍ତ ଡିଭାଇସ୍‍କୁ ପଠାଯାଇଥିବା ପ୍ୟାକେଟ୍‍ ପ୍ରାପ୍ତ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଅଣ-ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍‍ ତୁଳନାରେ ଏହା ଅଧିକ ପାୱାର୍‍ ବ୍ୟବହାର କରେ।" + "କେବଳ ଆପଣଙ୍କ ଫୋନ୍‍ ନୁହେଁ, ବରଂ ମଲ୍ଟିକାଷ୍ଟ ଠିକଣାଗୁଡ଼ିକ ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍‍ୱର୍କରେ ଥିବା ସମସ୍ତ ଡିଭାଇସ୍‌‍କୁ ପଠାଯିବା ପ୍ୟାକେଟ୍‍ଗୁଡ଼ିକ ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍‍ଟି ଅନୁମତି ଦେଇଥାଏ। ଅଣ-ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍‍ ତୁଳନାରେ ଏହା ଅଧିକ ପାୱାର୍‍ ବ୍ୟବହାର କରେ।" + "ବ୍ଲୁ-ଟୁଥ୍‍ ସେଟିଙ୍ଗ ଆକ୍ସେସ୍‌ କରନ୍ତୁ" + "ସ୍ଥାନୀୟ ବ୍ଲୁ-ଟୁଥ, ଟାବଲେଟ୍‍କୁ କନଫିଗର୍ କରିବାକୁ ଏବଂ ରିମୋର୍ଟ ଡିଭାଇସ୍‌କୁ ଚିହ୍ନାଇବା ତଥା ସେଗୁଡ଼ିକୁ ପେୟାର୍‍ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଆପ୍‌କୁ ସ୍ଥାନୀୟ ବ୍ଲୁ-ଟୁଥ୍ TVକୁ କନଫିଗର୍ କରିବାକୁ ଦେଇଥାଏ ଏବଂ ରିମୋର୍ଟ ଡିଭାଇସ୍‌କୁ ଚିହ୍ନାଇ ସେମାନଙ୍କୁ ଯୋଡ଼ିଥାଏ।" + "ସ୍ଥାନୀୟ ବ୍ଲୁ-ଟୁଥ, ଫୋନ୍‍କୁ କନଫିଗର୍ କରିବାକୁ ଏବଂ ରିମୋର୍ଟ ଡିଭାଇସ୍‌କୁ ଚିହ୍ନାଇବା ତଥା ସେଗୁଡ଼ିକୁ ପେୟାର୍‍ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "WiMAX ସହିତ ସଂଯୋଗ ଏବଂ ଏଥିରୁ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ" + "WiMAX ସକ୍ଷମ କି ନାହିଁ ସ୍ଥିର କରିବାକୁ ଏବଂ ସଂଯୁକ୍ତ ଥିବା କୌଣସି WiMAX ନେଟ୍‌ୱର୍କ ବିଷୟରେ ସୂଚନା ପାଇଁ ଆପ୍‌‍କୁ ଅନୁମତି ଦେଇଥାଏ ।" + "WiMAX ସ୍ଥିତିକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ" + "WiMAX ନେଟ୍‌ୱର୍କରୁ ଟାବଲେଟ୍‌ ସଂଯୋଗ କରିବାକୁ ଏବଂ ବିଚ୍ଛିନ୍ନ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଟିଭିକୁ ସଂଯୋଗ କରିବାକୁ ତଥା WiMAX ନେଟ୍‌ୱର୍କରୁ ଟିଭିକୁ ବିଚ୍ଛିନ୍ନ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ।" + "WiMAX ନେଟ୍‌ୱର୍କରୁ ଫୋନ୍ ସଂଯୋଗ କରିବାକୁ ଏବଂ ବିଚ୍ଛିନ୍ନ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ବ୍ଲୁ-ଟୁଥ୍‍ ଡିଭାଇସ୍‍ ଦେଖନ୍ତୁ" + "ଟାବଲେଟ୍‌ରେ ଥିବା ବ୍ଲୁ-ଟୁଥ୍‌ର କନଫିଗରେଶନ୍‍ ଦେଖିବାକୁ ଏବଂ ପେୟାର୍‍ କରାଯାଇଥିବା ଡିଭାଇସ୍‌ ସହିତ ସଂଯୋଗ ସ୍ୱୀକାର କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଟିଭିରେ ବ୍ଲୁ-ଟୁଥ୍‍ର କନଫିଗରେଶନ୍‍ ଦେଖିବାକୁ ଏବଂ ପେୟାର୍‍ ହୋଇଥିବା ଡିଭାଇସ୍‍ ସହ ସଂଯୋଗ ତିଆରି ତଥା ସ୍ୱୀକାର କରିବାକୁ ଆପ୍‍‍କୁ ଅନୁମତି ଦିଏ।" + "ଫୋନ୍‌ରେ ଥିବା ବ୍ଲୁ-ଟୁଥ୍‌ର କନଫିଗରେଶନ୍‍ ଦେଖିବାକୁ ଏବଂ ପେୟାର୍‍ କରାଯାଇଥିବା ଡିଭାଇସ୍‌ ସହିତ ସଂଯୋଗ ସ୍ୱୀକାର କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ନିଅର୍ ଫିଲ୍ଡ କମ୍ୟୁନିକେଶନ୍ ଉପରେ ନିୟନ୍ତ୍ରଣ ରଖନ୍ତୁ" + "ନିଅର୍‍ ଫିଲ୍ଡ କମ୍ୟୁନିକେସନ୍‍ନ (NFC) ଟାଗ୍‍, କାର୍ଡ ଓ ରିଡରଗୁଡ଼ିକ ସହ ଯୋଗାଯୋଗ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ଅକ୍ଷମ କରନ୍ତୁ" + "ଆପ୍‌କୁ କୀ\'ଲକ୍ କିମ୍ବା ସେଥିରେ ଥିବା କୌଣସି ପାସ୍‌ୱର୍ଡ ସୁରକ୍ଷାକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଅନୁମତି ଦିଏ, ଉଦାହରଣସ୍ୱରୂପ, ଇନ୍‌କମିଙ୍ଗ ଫୋନ୍‌ କଲ୍ ପ୍ରାପ୍ତ କରିବା ସମୟରେ ଫୋନ୍‌ଟି କୀ\'ଲକ୍‌କୁ ଅକ୍ଷମ କରିଦିଏ, ତା’ପରେ କଲ୍ ସମାପ୍ତ ହେବାପରେ ପୁଣି କୀ\'ଲକ୍‌କୁ ସକ୍ଷମ କରିଥାଏ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ପରିଚାଳନା କରନ୍ତୁ" + "ବ୍ୟବହାର କରିବା ପାଇଁ ଆଙ୍ଗୁଠି ଚିହ୍ନ ଯୋଡ଼ିବାକୁ ଓ ଡିଲିଟ୍‍ କରିବାକୁ ଆପକୁ ବିଧି ଆରମ୍ଭ କରିବାକୁ ଦେଇଥାଏ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ବ୍ୟବହାର କରନ୍ତୁ" + "ସ୍ୱୀକୃତି ପାଇଁ ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଂଶିକ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରୋସେସ୍‍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନ୍ସର୍‍ ମଇଳା ହୋଇଯାଇଛି। ଦୟାକରି ସଫା କରନ୍ତୁ ଓ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆଙ୍ଗୁଠି ବହୁତ ଜୋର୍‌ରେ ଚଲାଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆଙ୍ଗୁଠି ଖୁବ୍‍ ଧୀରେ ନିଆଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + + + "ଚିହ୍ନଟ ହେଲା ନାହିଁ" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ଉପଲବ୍ଧ ନାହିଁ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ଷ୍ଟୋର୍‍ କରାଯାଇପାରିବ ନାହିଁ। ଦୟାକରି ପୂର୍ବରୁ ଥିବା ଆଙ୍ଗୁଠି ଚିହ୍ନକୁ ବାହାର କରିଦିଅନ୍ତୁ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନର ସମୟ ଶେଷ ହେଲା । ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ କାର୍ଯ୍ୟ କ୍ୟାନ୍ସଲ୍‍ କରାଗଲା।" + + + "ବହୁତ ପ୍ରୟାସ କରାଗଲା। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + + + "ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + + + + + "ଆଙ୍ଗୁଠି %d" + + + "ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍" + "ସିଙ୍କ ସେଟିଙ୍ଗକୁ ପଢ଼ନ୍ତୁ" + "ଏକ ଆକାଉଣ୍ଟ ପାଇଁ ସିଙ୍କ ସେଟିଙ୍ଗ ପଢ଼ିବାକୁ ଆପ୍‍ଟିକୁ ଅନୁମତି ଦିଏ। ଉଦାହରଣସ୍ୱରୂପ, ଲୋକଙ୍କ ଆପ୍‍ ଏକ ଆକାଉଣ୍ଟରେ ସିଙ୍କ ହୋଇଛି କି ନାହିଁ ଏହା ଜାଣିପାରେ।" + "ସିଙ୍କ ଅନ୍‍ ଓ ଅଫ୍‍ ଟୋଗଲ୍‌ କରନ୍ତୁ" + "ଆପ୍‌କୁ ଏକ ଆକାଉଣ୍ଟ ପାଇଁ ସିଙ୍କ ସେଟିଙ୍ଗ ସଂଶୋଧନ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଉଦାହରଣସ୍ୱରୂପ, ଏହାର ବ୍ୟବହାର କୌଣସି ଆକାଉଣ୍ଟରେ ଥିବା ଲୋକଙ୍କ ଆପ୍‌ ସହିତ ସିଙ୍କ କରିବାକୁ ସକ୍ଷମ ହୋଇଥାଏ।" + "ସିଙ୍କ୍ ପରିସଂଖ୍ୟାନକୁ ପଢ଼ନ୍ତୁ।" + "ସିଙ୍କ କାର୍ଯ୍ୟର ହିଷ୍ଟୋରୀ ତଥା କେତେ ଡାଟା ସିଙ୍କ କରାଯାଇଛି, ସେଗୁଡ଼ିକ ଅନ୍ତର୍ଭୁକ୍ତ କରି, ଏକ ଆକାଉଣ୍ଟର ସିଙ୍କ ଅବସ୍ଥା ପଢ଼ିବା ପାଇଁ ଗୋଟିଏ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ।" + "ନିଜ USB ଷ୍ଟୋରେଜ୍‍ର କଣ୍ଟେଣ୍ଟ ପଢ଼ନ୍ତୁ" + "ନିଜ SD କାର୍ଡର କଣ୍ଟେଣ୍ଟ ପଢ଼ନ୍ତୁ" + "USB ଷ୍ଟୋରେଜ୍‍ରେ ଥିବା କଣ୍ଟେଣ୍ଟ ପଢିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଆପଣଙ୍କ SD କାର୍ଡରେ ଥିବା କଣ୍ଟେଣ୍ଟ ପଢ଼ିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଆପଣଙ୍କ USB ଷ୍ଟୋରେଜ୍‍ର କଣ୍ଟେଣ୍ଟ ସଂଶୋଧନ କରନ୍ତୁ କିମ୍ବା ଡିଲିଟ୍‍ କରନ୍ତୁ" + "ନିଜ SD କାର୍ଡର କଣ୍ଟେଣ୍ଟ ସଂଶୋଧନ କିମ୍ବା ଡିଲିଟ୍‍ କରନ୍ତୁ" + "ଆପ୍‍କୁ USB ଷ୍ଟୋରେଜରେ ଲେଖିବାକୁ ଦେଇଥାଏ।" + "ଆପ୍‍କୁ SD କାର୍ଡରେ ଲେଖିବାକୁ ଦେଇଥାଏ।" + "SIP କଲ୍‌ କରନ୍ତୁ ଏବଂ ଗ୍ରହଣ କରନ୍ତୁ" + "ଆପ୍‌କୁ SIP କଲ୍‌ କରିବାକୁ ଏବଂ ପାଇବାକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ନୂତନ ଦୂରସଂଚାର ସିମ୍ ସଂଯୋଗ ନଥିଭୁକ୍ତ କରନ୍ତୁ" + "ନୂତନ ଦୂରସଂଚାର ସିମ୍ ସଂଯୋଗକୁ ନଥିଭୁକ୍ତ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ।" + "ନୂତନ ଦୂରସଂଚାର ସଂଯୋଗ ନଥିଭୁକ୍ତ କରନ୍ତୁ" + "ନୂତନ ଦୂରସଂଚାର ସଂଯୋଗକୁ ନଥିଭୁକ୍ତ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ।" + "ଟେଲିକମ୍ ସଂଯୋଗ ପରିଚାଳିତ କରନ୍ତୁ" + "ଦୁରସଂଚାର ସଂଯୋଗ ପ୍ରବନ୍ଧିତ କରିବାକୁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ।" + "ଇନ୍ କଲ୍ ସ୍କ୍ରୀନ୍‍‌ ସହିତ ସଂଯୋଗ କରନ୍ତୁ" + "ୟୁଜର୍‍ କଲ୍ ଇନ୍ ସ୍କ୍ରୀନ୍‍‌ କେବେ ଓ କିପରି ଦେଖୁଛି, ତାହାକୁ ନିୟନ୍ତ୍ରିତ କରିବା ପାଇଁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ।" + "ଟେଲିଫୋନୀ ସେବା ସହିତ ସଂଯୋଗ କରନ୍ତୁ" + "ଆପ୍‌କୁ କଲ୍ କରିବା ଏବଂ ପ୍ରାପ୍ତ କରିବା ପାଇଁ ଟେଲିଫୋନୀ ସେବା ସହିତ ସଂଯୋଗ କରିବା ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ।" + "ଏକ କଲ୍ ୟୁଜର୍‍ ଅନୁଭବ ପ୍ରଦାନ କରିଥାଏ" + "ଆପ୍‌କୁ ଇନ୍‌-କ୍ଲଲ୍‌ ୟୁଜର୍‍ ଅନୁଭବ ନେବାକୁ ଦେଇଥାଏ।" + "ଐତିହାସିକ ନେଟୱର୍କ ଉପଯୋଗ ବିଷୟରେ ପଢ଼ନ୍ତୁ" + "ନିର୍ଦ୍ଦିଷ୍ଟ ନେଟୱର୍କ ତଥା ଆପ୍‍ଗୁଡ଼ିକ ପାଇଁ ବିଗତ ଦିନର ନେଟୱର୍କ ବ୍ୟବହାର ପଢ଼ିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ନେଟ୍‌ୱର୍କ ପଲିସୀକୁ ପରିଚାଳନା କରନ୍ତୁ" + "ନେଟୱର୍କ ପଲିସୀ ପରିଚାଳନା କରିବାକୁ ଏବଂ ଆପ୍‍-ନିର୍ଦ୍ଦିଷ୍ଟ ନିୟମ ଧାର୍ଯ୍ୟ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ନେଟ୍‌ୱର୍କ ବ୍ୟବହାର ଗଣନାକୁ ସଂଶୋଧନ କରନ୍ତୁ" + "ଆପ୍‍ ପାଇଁ କିପରି ନେଟ୍‍ୱର୍କ ବ୍ୟବହାର ଗଣନା କରାଯିବ, ସେଥିରେ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ। ସାମାନ୍ୟ ଆପ୍‍ ଦ୍ୱାରା ବ୍ୟବହାର କରାଯିବା ପାଇଁ ନୁହେଁ।" + "ବିଜ୍ଞପ୍ତି ଆକ୍ସେସ୍‌ କରନ୍ତୁ" + "ଅନ୍ୟ ଆପ୍ଲିକେଶନ୍‍ ଦ୍ୱାରା ପୋଷ୍ଟ କରାଯାଇଥିବାକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରି ବିଜ୍ଞପ୍ତିକୁ ହାସଲ, ପରୀକ୍ଷା, ତଥା ଖାଲି କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଏକ ବିଜ୍ଞପ୍ତି ଶ୍ରୋତା ସେବା ସହିତ ଯୋଡ଼ିହୁଅନ୍ତୁ" + "ଶ୍ରୋତା ସେବାର ଶୀର୍ଷ-ସ୍ତର ଇଣ୍ଟରଫେସ୍‍କୁ ବାନ୍ଧିରଖିବା ପଇଁ ଧାରକକୁ ଅନୁମତି ଦିଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବ ନାହିଁ।" + "ଏକ ସର୍ତ୍ତାବଳୀ ପ୍ରଦାନକାରୀ ସେବା ସହିତ ଯୋଡ଼ିହୁଅନ୍ତୁ" + "ଏକ ସ୍ଥିତି ପ୍ରଦାନକାରୀ ସେବାର ଶୀର୍ଷ-ସ୍ତର ଇଣ୍ଟରଫେସ୍‍ ବାନ୍ଧିରଖିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବନାହିଁ।" + "ଏକ ଡ୍ରୀମ୍‍ ସେବା ସହିତ ଯୋଡ଼ିହୁଅନ୍ତୁ" + "ଏକ ଡ୍ରୀମ୍‍ ସେବାର ଶୀର୍ଷ-ସ୍ତର ଇଣ୍ଟରଫେସ୍‍କୁ ବାନ୍ଧିରଖିବା ପାଇଁ ଧାରକକୁ ଅନୁମତି ଦିଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବନାହିଁ।" + "କେରିଅରଙ୍କ ଦ୍ୱାରା ଦିଆଯାଇଥିବା କନଫିଗରେଶନ୍‌ ଆପ୍‍ ଆରମ୍ଭ କରନ୍ତୁ" + "କେରିଅର୍‍ ଦ୍ୱାରା ଦିଆଯାଇଥିବା କନଫିଗରେଶନ୍‌ ଆପ୍‍ ରଖିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବନାହିଁ।" + "ନେଟ୍‍ୱର୍କ ସ୍ଥିତିର ନୀରିକ୍ଷଣକୁ ଶୁଣନ୍ତୁ" + "ନେଟ୍‍ୱର୍କ ସ୍ଥିତିରେ ନୀରିକ୍ଷଣ କରିବା ପାଇଁ ଗୋଟିଏ ଆପ୍ଲିକେଶନ୍‍କୁ ଅନୁମତି ଦିଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବ ନାହିଁ।" + "ଇନପୁଟ୍‍ ଡିଭାଇସ୍‍ କାଲିବରେଶନ୍‍ ବଦଳାନ୍ତୁ" + "ଟଚ୍‍ ସ୍କ୍ରୀନ୍‍ର କାଲିବରେଶନ୍‍ ପାରାମିଟର୍‌ ସଂଶୋଧନ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବନାହିଁ।" + "DRM ସର୍ଟିଫିକେଟ୍‍ ଆକ୍ସେସ୍‍ କରନ୍ତୁ" + "DRM ସର୍ଟିଫିକେଟ୍‍ର ପ୍ରାବଧାନ ତଥା ବ୍ୟବହାର କରିବା ପାଇଁ ଏକ ଆପ୍ଲିକେଶନ୍‍କୁ ଅନୁମତି ଦିଅନ୍ତୁ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବନାହିଁ।" + "Android ବିମ୍‍ ଟ୍ରାନ୍ସଫର୍‍ ଷ୍ଟାଟସ୍‍ ପ୍ରାପ୍ତ କରେ" + "କିଛି ସମୟ ପୂର୍ବରୁ ହୋଇଥିବା Android ବିମ୍‍ ଟ୍ରାନ୍ସଫର୍‍ ବିଷୟରେ ସୂଚନା ପ୍ରାପ୍ତ କରିବାକୁ ଏହି ଆପ୍ଲିକେଶନ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ" + "DRM ସର୍ଟିଫିକେଟ୍‌ ଅପସାରଣ କରନ୍ତୁ" + "DRM ସାର୍ଟିଫିକେଟ୍‍ କୁ ହଟାଇବା ପାଇଁ ଏକ ଆପ୍ଲିକେଶନ୍‍କୁ ଅନୁମତି ଦିଅନ୍ତୁ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହେବନାହିଁ।" + "ଏକ କେରିଅର୍‍ ମେସେଜିଙ୍ଗ ସେବା ସହିତ ଯୋଡ଼ିହୁଅନ୍ତୁ" + "ଏକ କେରିଅର୍‍ ମେସେଜିଙ୍ଗ ସେବାର ଶୀର୍ଷ-ସ୍ତରୀୟ ଇଣ୍ଟରଫେସ୍‍ ବାନ୍ଧିରଖିବାକୁ ହୋଲ୍ଡରଙ୍କୁ ଅନୁମତି ଦିଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ ଆବଶ୍ୟକ କରାଯିବା ଉଚିତ ନୁହେଁ।" + "କେରିଅର୍‍ ସେବାଗୁଡ଼ିକ ସହ ଯୋଡ଼ି ହୁଅନ୍ତୁ" + "କେରିଅର୍‌ ସେବାଗୁଡ଼ିକ ସହିତ ଧାରକଙ୍କୁ ଯୋଡ଼ିଥାଏ। ସାମାନ୍ୟ ଆପ୍‍ ପାଇଁ କଦାପି ଆବଶ୍ୟକ ହୁଏନାହିଁ।" + "\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଆକ୍ସେସ୍‍ କରନ୍ତୁ" + "\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" କନଫିଗରେଶନ୍‍ ପଢ଼ିବା ତଥା ଲେଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ପାସ୍‌ୱର୍ଡ ନିୟମାବଳୀ ସେଟ୍ କରନ୍ତୁ" + "ଲକ୍‍ ସ୍କ୍ରୀନ୍‍ ପାସ୍‌ୱର୍ଡ ଓ PINରେ ଅନୁମୋଦିତ ଦୀର୍ଘତା ଓ ବର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।" + + + "ସ୍କ୍ରୀନ୍‍ ଅନଲକ୍‍ କରିବାବେଳେ ଟାଇପ୍‍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍‍କୁ ଲକ୍‍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍‍ କରାଯାଇଥାଏ, ତେବେ ଟାବଲେଟ୍‍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।" + "ସ୍କ୍ରିନକୁ ଅନଲକ୍‌ କରିବା ସମୟରେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସ୍‌ୱର୍ଡ ସଂଖ୍ୟା ଉପରେ ନୀରିକ୍ଷଣ ରଖନ୍ତୁ ଏବଂ ଟିଭି ଲକ୍ କରନ୍ତୁ ଓ ଯଦି ଅନେକ ଭୁଲ ପାସ୍‌ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଛି, ତେବେ ସମସ୍ତ ଟିଭି ଡାଟାକୁ ହଟାଇଦିଅନ୍ତୁ।" + "ସ୍କ୍ରୀନ୍‍ ଅନଲକ୍‍ କରିବାବେଳେ ଟାଇପ୍‍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍‍କୁ ଲକ୍‍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍‍ କରାଯାଇଥାଏ, ତେବେ ଫୋନ୍‍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।" + "ସ୍କ୍ରୀନ୍‍ ଅନଲକ୍‍ କରିବାବେଳେ ଟାଇପ୍‍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍‌ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍‍କୁ ଲକ୍‍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍‌ୱର୍ଡ ଟାଇପ୍‍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।" + "ସ୍କ୍ରୀନ୍‍ ଅନଲକ୍‍ କରିବାବେଳେ ଟାଇପ୍‍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍‌ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟିଭିକୁ ଲକ୍‍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍‌ୱର୍ଡ ଟାଇପ୍‍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।" + "ସ୍କ୍ରୀନ୍‍ ଅନଲକ୍‍ କରିବାବେଳେ ଟାଇପ୍‍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍‌ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍‍କୁ ଲକ୍‍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍‌ୱର୍ଡ ଟାଇପ୍‍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।" + "ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ବଦଳାନ୍ତୁ" + "ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ବଦଳାନ୍ତୁ।" + "ସ୍କ୍ରୀନ୍‌କୁ ଲକ୍‌ କରନ୍ତୁ" + "ସ୍କ୍ରୀନ୍‍ କିପରି ଓ କେତେବେଳେ ଲକ୍‍ କରାଯିବ, ତାହା ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।" + "ସମସ୍ତ ଡାଟା ଖାଲି କରନ୍ତୁ" + "ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟାବ୍‍ଲେଟ୍‍ର ଡାଟା ଲିଭାଇଥାଏ।" + "ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟିଭିର ଡାଟା ଲିଭାଇଥାଏ।" + "ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଫୋନ୍‍ର ଡାଟା ଲିଭାଇଥାଏ।" + "ୟୁଜର୍‍ ଡାଟା ଲିଭାନ୍ତୁ" + "ବିନା ଚେତାବନୀରେ ଏହି ଟାବଲେଟରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।" + "ବିନା ଚେତାବନୀରେ ଏହି ଟିଭିରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।" + "ବିନା ଚେତାବନୀରେ ଏହି ଫୋନରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।" + "ଗ୍ଲୋବଲ୍ ପ୍ରକ୍ସୀ ଡିଭାଇସ୍‌କୁ ସେଟ୍ କରନ୍ତୁ" + "ପଲିସୀ ସକ୍ଷମ କରାଯାଇଥିବାବେଳେ ବ୍ୟବହାର କରିବା ପାଇଁ ଗ୍ଲୋବାଲ୍‍ ପ୍ରକ୍ସୀ ସେଟ୍‍ କରନ୍ତୁ। କେବଳ ଡିଭାଇସ୍‍ ମାଲିକ ଗ୍ଲୋବାଲ୍‍ ପ୍ରକ୍ସୀ ସେଟ୍‍ କରିପାରିବେ।" + "ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ପାସ୍‌ୱର୍ଡର ସମୟ ସମାପ୍ତି ସେଟ୍‍ କରନ୍ତୁ" + "ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ପାସ୍‍ୱର୍ଡ, PIN କିମ୍ବା ପାଟର୍ନ କେତେ ବ୍ୟବଧାନରେ ପରିବର୍ତ୍ତନ କରିବାକୁ ହେବ, ତାହା ପରିବର୍ତ୍ତନ କରନ୍ତୁ।" + "ଷ୍ଟୋରେଜ୍ ଏନକ୍ରିପସନ୍‌କୁ ସେଟ୍ କରନ୍ତୁ" + "ଷ୍ଟୋର୍ କରାଯାଇଥିବା ଡାଟା ଏନ୍‍କ୍ରେପ୍ଟ କରିବା ଆବଶ୍ୟକ।" + "କ୍ୟାମେରାଗୁଡ଼ିକୁ ଅକ୍ଷମ କରନ୍ତୁ" + "ସମସ୍ତ ଡିଭାଇସ୍‍ର କ୍ୟାମେରା ବ୍ୟବହାର କରିବା ପ୍ରତିରୋଧ କରନ୍ତୁ।" + "କିଛି ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ସୁବିଧାକୁ ଅକ୍ଷମ କରନ୍ତୁ" + "କିଛି ସ୍କ୍ରୀନ୍‍ ଲକ୍‍ ସୁବିଧା ବ୍ୟବହାର କରିବାରେ ପ୍ରତିରୋଧ କରନ୍ତୁ।" + + "ହୋମ୍" + "ମୋବାଇଲ୍‍" + "ୱାର୍କ" + "ୱାର୍କ ଫ୍ୟାକ୍ସ" + "ହୋମ୍ ଫାକ୍ସ" + "ପେଜର୍" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + + + "ହୋମ୍" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + + + "ହୋମ୍" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + + + "ହୋମ୍" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + + + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + + + "AIM" + "Windows Live" + "Yahoo" + "Skype" + "QQ" + "Google ଟକ୍‍" + "ICQ" + "Jabber" + + "କଷ୍ଟମ୍‌" + "ହୋମ୍" + "ମୋବାଇଲ୍‍" + "ୱାର୍କ" + "ୱାର୍କ ଫାକ୍ସ" + "ହୋମ୍ ଫାକ୍ସ" + "ପେଜର୍" + "ଅନ୍ୟାନ୍ୟ" + "କଲବ୍ୟାକ୍" + "କାର୍" + "କମ୍ପାନୀର ମୁଖ୍ୟ" + "ISDN" + "ମୁଖ୍ୟ" + "ଅନ୍ୟ ଫାକ୍ସ" + "ରେଡିଓ" + "ଟେଲେକ୍ସ" + "TTY TDD" + "ୱାର୍କ ମୋବାଇଲ୍" + "ୱାର୍କ ପେଜର୍" + "ସହାୟକ" + "MMS" + "କଷ୍ଟମ୍‌" + "ଜନ୍ମଦିନ" + "ଆନିଭର୍ସରୀ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + "ହୋମ୍" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "ମୋବାଇଲ୍‍" + "କଷ୍ଟମ୍‌" + "ହୋମ୍" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + "ହୋମ୍" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + "AIM" + "Windows Live" + "Yahoo" + "Skype" + "QQ" + "ହ୍ୟାଙ୍ଗଆଉଟ୍ସ" + "ICQ" + "Jabber" + "NetMeeting" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "କଷ୍ଟମ୍‌" + "କଷ୍ଟମ୍‌" + "ସହାୟକ" + "ଭାଇ" + "ଶିଶୁ" + "ଡୋମେଷ୍ଟିକ୍‌ ପାର୍ଟନର୍‌" + "ପିତା" + "ସାଙ୍ଗ" + "ମ୍ୟାନେଜର୍‍" + "ମାତା" + "ଅଭିଭାବକ" + "ସହଭାଗୀ" + "ରେଫର୍‍ କରିଛନ୍ତି" + "ସମ୍ପର୍କୀୟ" + "ଭଉଣୀ" + "ସ୍ଵାମୀ ବା ସ୍ତ୍ରୀ" + "କଷ୍ଟମ୍‌" + "ଘର" + "ୱାର୍କ" + "ଅନ୍ୟାନ୍ୟ" + "ଏହି ଯୋଗାଯୋଗ ଦେଖିବାକୁ କୌଣସି ଆପ୍ଲିକେଶନ୍‍ ମିଳିଲା ନାହିଁ।" + "PIN କୋଡ୍‍ ଟାଇପ୍‍ କରନ୍ତୁ" + "PUK ଓ ନୂଆ PIN କୋଡ୍‍ ଟାଇପ୍‍ କରନ୍ତୁ" + "PUK କୋଡ୍‍" + "ନୂଆ PIN କୋଡ୍‍" + "ପାସ୍‌ୱର୍ଡ ଟାଇପ୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ" + "ଅନଲକ୍‍ କରିବାକୁ ପାସୱର୍ଡ ଟାଇପ୍‍ କରନ୍ତୁ" + "ଅନଲକ୍‍ କରିବାକୁ PIN ଟାଇପ୍‍ କରନ୍ତୁ" + "ଭୁଲ PIN କୋଡ୍।" + "ଅନଲକ୍ କରିବା ପାଇଁ, ପ୍ରଥମେ ମେନୁ ଓ ତା’ପରେ 0 ଦବାନ୍ତୁ।" + "ଜରୁରୀକାଳୀନ ନମ୍ବର୍‌" + "କୌଣସି ସେବା ନାହିଁ" + "ସ୍କ୍ରୀନ୍‌ ଲକ୍‌ ହୋଇଯାଇଛି।" + "ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ କିମ୍ବା ଜରୁରୀକାଳୀନ କଲ୍‌ କରନ୍ତୁ।" + "ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।" + "ଅନଲକ୍‌ କରିବା ପାଇଁ ପାଟର୍ନ ଆଙ୍କନ୍ତୁ" + "ଜରୁରୀକାଳୀନ" + "କଲ୍‌କୁ ଫେରନ୍ତୁ" + "ଠିକ୍!" + "ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ" + "ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ" + "ସମସ୍ତ ସୁବିଧା ତଥା ଡାଟା ପାଇଁ ଅନଲକ୍‍ କରନ୍ତୁ" + "ମାଲିକର ମୁହଁ ଚିହ୍ନି ଅନଲକ୍‍ କରିବାର ସର୍ବାଧିକ ଧାର୍ଯ୍ୟ ସୀମା ଅତିକ୍ରମ କଲା" + "କୌଣସି SIM କାର୍ଡ ନାହିଁ" + "ଟାବଲେଟ୍‌ରେ କୌଣସି SIM‍ କାର୍ଡ ନାହିଁ।" + "ଟିଭିରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।" + "ଫୋନ୍‌ରେ କୌଣସି SIM‍ କାର୍ଡ ନାହିଁ।" + "ଏକ SIM କାର୍ଡ ଭର୍ତ୍ତି କରନ୍ତୁ।" + "SIM କାର୍ଡ ନାହିଁ କିମ୍ବା ଖରାପ ଅଛି। SIM କାର୍ଡ ଭର୍ତ୍ତି କରନ୍ତୁ।" + "SIM କାର୍ଡଟି ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ।" + "ଆପଣଙ୍କ SIM କାର୍ଡକୁ ସ୍ଥାୟୀ ରୂପେ ଅକ୍ଷମ କରିଦିଆଯାଇଛି।\n ଅନ୍ୟ SIM କାର୍ଡ ପାଇଁ ଆପଣଙ୍କ ୱାୟରଲେସ୍‍ ସେବା ପ୍ରଦାନକାରୀଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।" + "ପୂର୍ବବର୍ତ୍ତୀ ଟ୍ରାକ୍‌" + "ପରବର୍ତ୍ତୀ ଟ୍ରାକ୍‌" + "ପଜ୍‍ କରନ୍ତୁ" + "ପ୍ଲେ କରନ୍ତୁ" + "ବନ୍ଦ କରନ୍ତୁ" + "ପଛକୁ ନିଅନ୍ତୁ" + "ଫାଷ୍ଟ ଫ‌ର୍‌ୱାର୍ଡ" + "କେବଳ ଜରୁରୀକାଳୀନ କଲ୍‌" + "ନେଟ୍‌ୱର୍କକୁ ଲକ୍‌ କରାଯାଇଛି" + "SIM କାର୍ଡଟିରେ PUK ଲକ୍‍ ହୋଇଯାଇଛି।" + "ୟୁଜର୍‍ ଗାଇଡ୍‍ ଦେଖନ୍ତୁ କିମ୍ବା ଗ୍ରାହକ ସେବା କେନ୍ଦ୍ର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।" + "SIM କାର୍ଡ ଲକ୍‍ ହୋଇଯାଇଛି" + "SIM କାର୍ଡକୁ ଅନଲକ୍‍ କରାଯାଉଛି…" + "ଆପଣଙ୍କ ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। \n\n%2$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣଙ୍କ ପାସୱର୍ଡକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଟାଇପ୍‍ କରିଛନ୍ତି। \n\n%2$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣଙ୍କ PINକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଟାଇପ୍‍ କରିଛନ୍ତି। \n\n%2$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, Google ସାଇନ୍‌-ଇନ୍‍ ବ୍ୟବହାର କରି ଆପଣଙ୍କୁ ନିଜ ଟାବଲେଟ୍‍କୁ ଅନଲକ୍‍ କରିବାକୁ କୁହାଯିବ।\n\n %3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣ ନିଜର ଲକ୍‌ ଖୋଲିବା ପାଟର୍ନକୁ %1$d ଥର ଭୁଲ ଆଙ୍କିଛନ୍ତି। ଅଧିକ %2$d ଅସଫଳ ଚେଷ୍ଟା ପରେ ଆପଣଙ୍କ Google ସାଇନ୍-ଇନ୍ର ବ୍ୟବହାର କରି ନିଜ TV କୁ ଅନଲକ୍‌ କରିବା ପାଇଁ କୁହାଯିବ। \n\n%3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, Google ସାଇନ୍‌-ଇନ୍‍ ବ୍ୟବହାର କରି ଆପଣଙ୍କୁ ନିଜ ଫୋନ୍‍କୁ ଅନଲକ୍‍ କରିବାକୁ କୁହାଯିବ।\n\n %3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଟାବଲେଟ୍‍କୁ ଅନ୍‌ଲକ୍‌ କରିବା ପାଇଁ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଟାବଲେଟ୍‍ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ ଏବଂ ଆପଣ ସମସ୍ତ ୟୁଜର୍‍ ଡାଟା ହରାଇବେ।" + "ଟିଭିକୁ ଅନଲକ୍‍ କରିବା ପାଇଁ ଆପଣ %1$d ଥର ଭୁଲ୍‍ ପ୍ରୟାସ କଲେ। ଆଉ %2$d ଟି ଭୁଲ୍‍ ପ୍ରୟାସ ପରେ, ଟିଭିଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ ଏବଂ ସମସ୍ତ ୟୁଜର୍‍ ଡାଟା ବାହାରିଯିବ।" + "ଫୋନ୍‍ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଫୋନ୍‍ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ ଏବଂ ଆପଣ ସମସ୍ତ ୟୁଜର୍‍ ଡାଟା ହରାଇବେ।" + "ଟାବଲେଟ୍‍ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଟାବଲେଟ୍‍ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ।" + "ଟିଭିକୁ ଅନଲକ୍‍ କରିବା ପାଇଁ ଆପଣ %d ଥର ଭୁଲ୍‍ ପ୍ରୟାସ କଲେ। ଟିଭିଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ।" + "ଫୋନ୍‍ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଫୋନ୍‍ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ।" + "%d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ପାଟର୍ନ ଭୁଲି ଯାଇଛନ୍ତି କି?" + "ଆକାଉଣ୍ଟ ଅନଲକ୍‌" + "ଅତ୍ୟଧିକ ପାଟର୍ନ ଉଦ୍ୟମ" + "ଅନଲକ୍‌ କରିବା ପାଇଁ, ନିଜ Google ଆକାଉଣ୍ଟରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ।" + "ୟୁଜରନେମ୍‌ (ଇମେଲ୍)" + "ପାସ୍‌ୱର୍ଡ:" + "ସାଇନ୍‍-ଇନ୍" + "ୟୁଜର୍‍ନେମ୍‌ କିମ୍ୱା ପାସ୍‌ୱର୍ଡ ଭୁଲ୍‍ ଅଛି।" + "ଆପଣଙ୍କର ଯୁଜରନେମ୍‌ କିମ୍ୱା ପାସୱାର୍ଡ ଭୁଲି ଯାଇଛନ୍ତି କି?\n""google.com/accounts/recovery"" ଭିଜିଟ୍‍ କରନ୍ତୁ।" + "ଯାଞ୍ଚ କରାଯାଉଛି…" + "ଅନଲକ୍‌" + "ସାଉଣ୍ଡ ଅନ୍ ଅଛି" + "ସାଉଣ୍ଡ ଅଫ୍ ଅଛି" + "ପାଟର୍ନ ଆରମ୍ଭ ହେଲା" + "ପାଟର୍ନ ଖାଲି କରାଗଲା" + "ସେଲ୍‍ ଯୋଡ଼ାଗଲା" + "ସେଲ୍‍ %1$s ଯୋଡ଼ାଗଲା" + "ପାଟର୍ନ ସମ୍ପୂର୍ଣ୍ଣ ହେଲା" + "ପାଟର୍ନ କ୍ଷେତ୍ର।" + "%1$s। %3$dରୁ %2$d ୱିଜେଟ୍‍।" + "ୱିଜେଟ୍ ଯୋଡ଼ନ୍ତୁ।" + "ଖାଲି କରନ୍ତୁ" + "ଅନଲକ୍‍ କ୍ଷେତ୍ରକୁ ବଢ଼ାଇଦିଆଯାଇଛି।" + "ଅନଲକ୍‍ କ୍ଷେତ୍ର ଛୋଟ କରିଦିଆଯାଇଛି।" + "%1$s ୱିଜେଟ୍‍।" + "ୟୁଜର୍‌ ଚୟନକାରୀ" + "ସ୍ଥିତି" + "କ୍ୟାମେରା" + "ମିଡିଆ ନିୟନ୍ତ୍ରଣ" + "ୱିଜେଟ୍‍ ପୁନଃ ସଜାଇବା ଆରମ୍ଭ ହେଲା।" + "ୱିଜେଟ୍‍ ପୁନଃ ସଜାଇବା ଶେଷ ହେଲା।" + "%1$s ୱିଜେଟ୍‍ ଡିଲିଟ୍‍ କରିଦିଆଗଲା।" + "ଅନଲକ୍‍ କ୍ଷେତ୍ରକୁ ବଢ଼ାନ୍ତୁ।" + "ସ୍ଲାଇଡ୍‍ ଅନଲକ୍‍।" + "ପାଟର୍ନ ଅନଲକ୍‍।" + "ଫେସ୍‍ ଅନଲକ୍‍।" + "PIN ଅନଲକ୍‍।" + + + + + "ପାସ୍‍ୱର୍ଡ ଅନଲକ୍‍।" + "ପାଟର୍ନ କ୍ଷେତ୍ର।" + "ସ୍ଲାଇଡ୍‍ କ୍ଷେତ୍ର।" + "?123" + "ABC" + "ALT" + "ବର୍ଣ୍ଣ" + "ଶବ୍ଦ" + "ଲିଙ୍କ" + "ରେଖା" + "ଫ୍ୟାକ୍ଟୋରୀ ଟେଷ୍ଟ ବିଫଳ ହୋଇଛି" + "FACTORY_TEST କାର୍ଯ୍ୟ କରିବା ପାଇଁ /system/appରେ ଇନଷ୍ଟଲ୍‌ ହୋଇଥିବା ପ୍ୟାକେଜ୍‌ ପାଇଁ କେବଳ ସପୋର୍ଟ କରେ।" + "FACTORY_TEST କାର୍ଯ୍ୟ କରିବା ପାଇଁ କୌଣସି ପ୍ୟାକେଜ୍ ମିଲିଲା ନାହିଁ।" + "ରିବୁଟ୍ କରନ୍ତୁ" + "\"%s\" ରେ ଥିବା ପୃଷ୍ଠାଟି କୁହେ:" + "ଜାଭାସ୍କ୍ରୀପ୍ଟ" + "ନେଭିଗେଶନ୍ ସୁନିଶ୍ଚିତ କରନ୍ତୁ" + "ଏହି ପୃଷ୍ଠାରୁ ବାହାରିଯାଆନ୍ତୁ" + "ଏହି ପୃଷ୍ଠାରେ ରୁହନ୍ତୁ" + "%s\n\nଆପଣ କ’ଣ ପ୍ରକୃତରେ ଏହି ପୃଷ୍ଠାରୁ ଦୂରକୁ ନେଭିଗେଟ୍‍ କରିବାକୁ ଚାହୁଁଛନ୍ତି?" + "ନିଶ୍ଚିତ କରନ୍ତୁ" + "ଧ୍ୟାନଦିଅନ୍ତୁ: ଜୁମ୍‌ ଇନ୍‍ ଓ ଆଉଟ୍‍ କରିବା ପାଇଁ ଡବଲ୍‍-ଟାପ୍‌ କରନ୍ତୁ" + "ସ୍ୱତଃପୂରଣ" + "ଅଟୋଫିଲ୍ ସେଟ୍ କରନ୍ତୁ" + + + " " + "$1$2$3" + ", " + "$1$2$3" + "ଅଞ୍ଚଳ" + "ପୋଷ୍ଟାଲ୍ କୋଡ୍" + "ରାଜ୍ୟ" + "ZIP କୋଡ୍" + "କାଉଣ୍ଟୀ" + "ଆଇଲ୍ୟାଣ୍ଡ" + "ଜିଲ୍ଲା" + "ବିଭାଗ" + "ପ୍ରଶାସକୀୟ କ୍ଷେତ୍ର" + "ପ୍ୟାରିସ୍‌" + "କ୍ଷେତ୍ର" + "ଭୂଖଣ୍ଡ" + "ଆପଣଙ୍କ ୱେବ୍‍ ବୁକ୍‍ମାର୍କ ଓ ହିଷ୍ଟୋରୀ ପଢ଼ନ୍ତୁ" + "ବ୍ରାଉଜର୍‍ରେ ଭିଜିଟ୍‍ କରାଯାଇଥିବା ସମସ୍ତ URL ପଢ଼ିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଧ୍ୟାନଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍‍ ବ୍ରାଉଜ୍‍ କରିବା ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍‌ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେଶନ୍‍‍ରେ ଲାଗୁ କରାଯାଇନପାରେ।" + "ୱେବ୍‍ ବୁକ୍‍ମାର୍କ ଓ ହିଷ୍ଟୋରୀ ଲେଖନ୍ତୁ" + "ଆପଣଙ୍କ ଟାବ୍‍ଲେଟ୍‍ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍‍ ହିଷ୍ଟୋରୀ କିମ୍ବା ବୁକ୍‍ମାର୍କଗୁଡ଼ିକ ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହାଦ୍ୱାରା ଆପ୍‍ଟି ବ୍ରାଉଜର୍‍ ଡାଟା ଲିଭାଇପାରେ କିମ୍ବା ବଦଳାଇପାରେ। ଧ୍ୟାନଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍‍ ବ୍ରାଉଜ୍‍ କରିବାର ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍‌ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେଶନ୍‍‍ରେ ଲାଗୁ କରାଯାଇନପାରେ।" + "ଆପଣଙ୍କ ଟିଭିରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍‌ ହିଷ୍ଟୋରୀ କିମ୍ବା ବୁକମାର୍କଗୁଡ଼ିକ ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହାଦ୍ୱାରା ଆପ୍‍ଟି ବ୍ରାଉଜର୍ ଡାଟା ଲିଭାଇପାରେ କିମ୍ବା ବଦଳାଇପାରେ। ଧ୍ୟାଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍‍ ବ୍ରାଉଜ୍‍ କରିବା ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍‌ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେ୍ନ୍‍‍ଶରେ ଲାଗୁ କରାଯାଇନପାରେ।" + "ଆପଣଙ୍କ ଫୋନ୍‍ରେ ଷ୍ଟୋର୍‍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍‍ ହିଷ୍ଟୋରୀ କିମ୍ବା ବୁକ୍‍ମାର୍କଗୁଡ଼ିକ ବଦଳାଇବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଏହାଦ୍ୱାରା ଆପ୍‍ଟି ବ୍ରାଉଜର୍‍ ଡାଟା ଲିଭାଇପାରେ କିମ୍ବା ବଦଳାଇପାରେ। ଧ୍ୟାନଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍‍ ବ୍ରାଉଜ୍‍ କରିବାର ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍‌ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେଶନ୍‍‍ରେ ଲାଗୁ କରାଯାଇନପାରେ।" + "ଏକ ଆଲର୍ମ ସେଟ୍‍ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ଇନଷ୍ଟଲ୍‍ ହୋଇଥିବା ଆଲାର୍ମ କ୍ଲକ୍‍ ଆପ୍‍ରେ ଏକ ଆଲାର୍ମ ସେଟ୍‍ କରିବାକୁ ଦେଇଥାଏ। କିଛି ଆଲର୍ମ କ୍ଲକ୍ ଆପ୍‍ ଏହି ବୈଶିଷ୍ଟ୍ୟ ଲାଗୁ କରିନପାରନ୍ତି।" + "ଭଏସ୍‌ମେଲ୍‌ ଯୋଡ଼ନ୍ତୁ" + "ଆପଣଙ୍କ ଭଏସମେଲ୍‍ ଇନ୍‌ବକ୍ସରେ ମେସେଜ୍‍ ଯୋଡ଼ିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ।" + "ବ୍ରାଉଜରର ଭୌଗଳିକ ଅନୁମତି ସଂଶୋଧନ କରନ୍ତୁ" + "ଆପ୍‍କୁ, ବ୍ରାଉଜର୍‍ର ଭୌଗଳିକ ଅନୁମତି ବଦଳାଇବାକୁ ଦେଇଥାଏ। ହାନୀକାରକ ଆପ୍‍ ଆର୍ବିଟେରୀ ୱେବ୍‍ ସାଇଟଗୁଡ଼ିକୁ ଲୋକେଶନ୍‍ ସୂଚନା ପଠାଇବା ପାଇଁ ଏହା ବ୍ୟବହାର କରିପାରନ୍ତି।" + "ବ୍ରାଉଜର୍ ଏହି ପାସୱର୍ଡକୁ ମନେରଖୁ ବୋଲି ଆପଣ ଚାହୁଁଛନ୍ତି କି?" + "ବର୍ତ୍ତମାନ ନୁହେଁଁ" + "ମନେରଖନ୍ତୁ" + "କଦାପି ନୁହେଁ" + "ଏହି ପୃଷ୍ଠା ଖୋଲିବାକୁ ଆପଣଙ୍କ ପାଖରେ ଅନୁମତି ନାହିଁ।" + "ଟେକ୍ସଟ୍‍ କ୍ଲିପବୋର୍ଡକୁ କପୀ ହୋଇଯାଇଛି" + "ଅଧିକ" + "ମେନୁ" + "ସ୍ପେସ୍‍" + "ଏଣ୍ଟର୍" + "ଡିଲିଟ୍‍" + "ସର୍ଚ୍ଚ କରନ୍ତୁ" + "ସର୍ଚ୍ଚ…" + "ସର୍ଚ୍ଚ କରନ୍ତୁ" + "କ୍ୱେରୀ ସର୍ଚ୍ଚ କରନ୍ତୁ" + "କ୍ୱେରୀ ଖାଲି କରନ୍ତୁ" + "କ୍ୱେରୀ ଦାଖଲ କରନ୍ତୁ" + "ଭଏସ୍‍ ସର୍ଚ୍ଚ" + "’ସ୍ପର୍ଶ କରି ଏକ୍ସପ୍ଲୋର୍‍ କରନ୍ତୁ’ ସକ୍ଷମ କରିବେ?" + "%1$s ’ସ୍ପର୍ଶ କରି ଏକ୍ସପ୍ଲୋର୍ କରନ୍ତୁ’ ସକ୍ଷମ କରିବାକୁ ଚାହେଁ। ’ସ୍ପର୍ଶ କରି ଏକ୍ସପ୍ଲୋର୍ କରନ୍ତୁ’ ଅନ୍‌ ଥିବାବେଳେ, ଆପଣଙ୍କ ଆଙ୍ଗୁଠି ତଳେ କ’ଣ ଅଛି, ତାହାର ବ୍ୟାଖ୍ୟା ଦେଖିପାରିବେ କିମ୍ବା ଟାବ୍‍ଲେଟ୍‍ ସହ କଥାବାର୍ତ୍ତା କରିବାକୁ ଜେଶ୍ଚର୍‌ କରିପାରିବେ।" + "%1$s ’ସ୍ପର୍ଶ କରି ଏକ୍ସପ୍ଲୋର୍ କରନ୍ତୁ’ ସକ୍ଷମ କରିବାକୁ ଚାହେଁ। ’ସ୍ପର୍ଶ କରି ଏକ୍ସପ୍ଲୋର୍ କରନ୍ତୁ’ ଅନ୍‌ ଥିବାବେଳେ, ଆପଣଙ୍କ ଆଙ୍ଗୁଠି ତଳେ କ’ଣ ଅଛି, ତାହାର ବ୍ୟାଖ୍ୟା ଦେଖିପାରିବେ କିମ୍ବା ଫୋନ୍‍ ସହ କଥାବାର୍ତ୍ତା କରିବାକୁ ଜେଶ୍ଚର୍‌ କରିପାରିବେ।" + "1 ମାସ ପୂର୍ବରୁ" + "1 ମାସ ପୂର୍ବରୁ" + + ଶେଷ %d ଦିନ + ଶେଷ %d ଦିନ + + "ଶେଷ ମାସ" + "ପୁରାତନ" + "%sରେ" + "%sରେ" + "%sରେ" + "ଦିନ" + "ଦିନ" + "ଘଣ୍ଟା" + "ଘଣ୍ଟା" + "ମିନିଟ୍‍" + "ମିନିଟ୍‍" + "ସେକେଣ୍ଡ" + "ସେକେଣ୍ଡ" + "ସପ୍ତାହ" + "ସପ୍ତାହ" + "ବର୍ଷ" + "ବର୍ଷ" + "ବର୍ତ୍ତମାନ" + + %dମିନିଟ୍‍ + %dମିନିଟ୍‍ + + + %dଘଣ୍ଟା + %dଘଣ୍ଟା + + + %dଦିନ + %dଦିନ + + + %dବର୍ଷ + %dବର୍ଷ + + + %dମିନିଟରେ + %dମିନିଟରେ + + + %d ଘଣ୍ଟାରେ + %d ଘଣ୍ଟାରେ + + + %dଦିନରେ + %dଦିନରେ + + + %dବର୍ଷରେ + %dବର୍ଷରେ + + + %d ମିନିଟ୍‍ ପୂର୍ବେ + %d ମିନିଟ୍‍ ପୂର୍ବେ + + + %d ଘଣ୍ଟା ପୂର୍ବେ + %d ଘଣ୍ଟା ପୂର୍ବେ + + + %d ଦିନ ପୂର୍ବେ + %d ଦିନ ପୂର୍ବେ + + + %d ବର୍ଷ ପୂର୍ବେ + %d ବର୍ଷ ପୂର୍ବେ + + + %d ମିନିଟରେ + %d ମିନିଟରେ + + + %d ଘଣ୍ଟାରେ + %d ଘଣ୍ଟାରେ + + + %d ଦିନରେ + %d ଦିନରେ + + + %d ବର୍ଷରେ + %d ବର୍ଷରେ + + "ଭିଡିଓ ସମସ୍ୟା" + "ଏହି ଡିଭାଇସ୍‍କୁ ଷ୍ଟ୍ରିମ୍‍ କରିବା ପାଇଁ ଏହି ଭିଡିଓ ମାନ୍ୟ ନୁହେଁ।" + "ଏହି ଭିଡିଓ ଚଲାଇ ହେବନାହିଁ" + "ଠିକ୍‍ ଅଛି" + "%1$s %2$s" + "ମଧ୍ୟାହ୍ନ" + "ମଧ୍ୟାହ୍ନ" + "ମଧ୍ୟରାତ୍ର" + "ମଧ୍ୟରାତ୍ର" + "%1$02d:%2$02d" + "%1$d:%2$02d:%3$02d" + "ସବୁ ଚୟନ କରନ୍ତୁ" + "କଟ୍‌" + "କପୀ" + + + "ପେଷ୍ଟ କରନ୍ତୁ" + "ସାଦା ଟେକ୍ସଟ୍‍ ଭାବରେ ପେଷ୍ଟ କରନ୍ତୁ" + "ବଦଳାନ୍ତୁ…" + "ଡିଲିଟ୍‍ କରନ୍ତୁ" + "URL କପୀ କରନ୍ତୁ" + "ଟେକ୍ସଟ୍‍ ଚୟନ କରନ୍ତୁ" + "ପୂର୍ବ ପରି କରନ୍ତୁ" + "ପୁଣି କରନ୍ତୁ" + + + "ଟେକ୍ସଟ୍‍ ଚୟନ" + "ଶବ୍ଦକୋଷରେ ଯୋଡ଼ନ୍ତୁ" + "ଡିଲିଟ୍‍ କରନ୍ତୁ" + "ଇନପୁଟ୍ ପଦ୍ଧତି" + "ଟେକ୍ସଟ୍‌ କାର୍ଯ୍ୟ" + + + + + + + + + + + + + + + + + + + "ଷ୍ଟୋରେଜ୍‌ ସ୍ପେସ୍‌ ଶେଷ ହେବାରେ ଲାଗିଛି" + "କିଛି ସିଷ୍ଟମ ଫଙ୍କଶନ୍‍ କାମ କରିନପାରେ" + "ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍‌ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।" + "%1$s ଚାଲୁଛି" + "ଅଧିକ ସୂଚନା ପାଇଁ କିମ୍ବା ଆପ୍‍ ବନ୍ଦ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।" + "ଠିକ୍‍ ଅଛି" + "କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ" + "ଠିକ୍‍ ଅଛି" + "କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ" + "ଧ୍ୟାନଦିଅନ୍ତୁ" + "ଲୋଡ୍ କରାଯାଉଛି…" + "ଅନ୍" + "ଅଫ୍" + "ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ" + "%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ" + + + "ସହିତ ଖୋଲନ୍ତୁ" + "%1$s ସହିତ ଖୋଲନ୍ତୁ" + + + "ସହିତ ଏଡିଟ୍‌ କରନ୍ତୁ" + "%1$sରେ ସଂଶୋଧନ କରନ୍ତୁ" + + + "ଏହାଙ୍କ ସହ ଶେୟାର୍‌ କରନ୍ତୁ" + "%1$s ସହିତ ଶେୟାର୍‌ କରନ୍ତୁ" + + + + + + + + + "ହୋମ୍‍ ଆପ୍‌ ଚୟନ କରନ୍ତୁ" + "ହୋମ୍‍ ରୂପରେ %1$s ବ୍ୟବହାର କରନ୍ତୁ" + + + + + + + + + "ଏହି କାର୍ଯ୍ୟ ପାଇଁ ଡିଫଲ୍ଟ ଭାବେ ବ୍ୟବହାର କରନ୍ତୁ।" + "ଏକ ଭିନ୍ନ ଆପ୍‌ର ବ୍ୟବହାର କରନ୍ତୁ" + "\"ସିଷ୍ଟମ୍‍ ସେଟିଙ୍ଗ > ଆପ୍‍ > ଡାଉନଲୋଡ୍‍ କରାଯାଇଛି\"ରେ ଡିଫଲ୍ଟ ଖାଲି କରନ୍ତୁ।" + "ଏକ କାର୍ଯ୍ୟାନୁଷ୍ଠାନ ବାଛନ୍ତୁ" + "USB ଡିଭାଇସ୍‍ ପାଇଁ ଗୋଟିଏ ଆପ୍‍ ବାଛନ୍ତୁ" + "କୌଣସି ଆପ୍‍ ଏହି କାର୍ଯ୍ୟ କରିପାରିବ ନାହିଁ।" + "%1$s ବନ୍ଦ ହୋଇଯାଇଛି" + "%1$s ବନ୍ଦ ହୋଇଯାଇଛି" + + + + + "ଆପ୍‌କୁ ପୁଣି ଖୋଲନ୍ତୁ" + "ମତାମତ ପଠାନ୍ତୁ" + "ବନ୍ଦ କରନ୍ତୁ" + + + + + + + + + + + + + + + + "ଠିକ୍‍ ଅଛି" + "ରିପୋର୍ଟ" + "ଅପେକ୍ଷା କରନ୍ତୁ" + "ଏହି ପୃଷ୍ଠାଟି ଚାଲୁନାହିଁ।\n\nଆପଣ ଏହାକୁ ବନ୍ଦ କରିବେ କି?" + "ଆପ୍‌କୁ ରିଡାଇରେକ୍ଟ କରାଗଲା" + "%1$s ଏବେ ଚାଲୁଅଛି।" + "%1$sକୁ ବାସ୍ତବିକ ରୂପରେ ଲଞ୍ଚ କରାଯାଇଥିଲା।" + "ସ୍କେଲ୍" + "ସର୍ବଦା ଦେଖାନ୍ତୁ" + "\"ସିଷ୍ଟମ୍‍ ସେଟିଙ୍ଗ > ଆପ୍‍ > ଡାଉନଲୋଡ୍‍ କରାଯାଇଛି\"ରେ ଏହାକୁ ପୁନଃ-ସକ୍ଷମ କରନ୍ତୁ।" + "%1$s ବର୍ତ୍ତମାନର ଡିସ୍‌ପ୍ଲେ ଆକାର ସେଟିଙ୍ଗ ସପୋର୍ଟ କରେନାହିଁ ଏବଂ ଅପ୍ରତ୍ୟାଶିତ କାର୍ଯ୍ୟ କରିପାରେ।" + "ସର୍ବଦା ଦେଖାନ୍ତୁ" + + + + + + + "ଆପ୍‍ %1$s (ପ୍ରକ୍ରିୟା %2$s) ଏହାର ସ୍ୱ-ଲାଗୁ କରାଯାଇଥିବା ଷ୍ଟ୍ରିକ୍ଟ-ମୋଡ୍‍ ପଲିସୀ ଉଲ୍ଲଂଘନ କରିଛି।" + "ଏହି {0/PROCESS%1$s ନିଜ ଦ୍ୱାରା ଲାଗୁ କରାଯାଇଥିବା ଷ୍ଟ୍ରିକ୍ଟମୋଡ୍‌ ପଲିସୀକୁ ଉଲ୍ଲଂଘନ କରିଛି।" + + + + + + + + + + + + + "ଷ୍ଟୋରେଜ୍‍ ବଢ଼ାଯାଉଛି" + + + "%1$sକୁ ଅପଗ୍ରେଡ୍‍ କରାଯାଉଛି…" + "%2$dରୁ %1$d ଆପ୍‍ ଅନୁକୂଳନ କରୁଛି।" + "%1$s ପ୍ରସ୍ତୁତ କରାଯାଉଛି।" + "ଆପ୍‍ ଆରମ୍ଭ କରାଯାଉଛି।" + "ବୁଟ୍‍ ସମାପ୍ତ କରୁଛି।" + "%1$s ଚାଲୁଛି" + + + + + + + + + + + + + "%1$s ଧାର୍ଯ୍ୟ ମେମୋରୀରୁ ବାହାରକୁ ଗଲା" + "ହିପ୍‍ ଡମ୍ପ ସଂଗ୍ରହ କରାଯାଇଛି; ଶେୟାର୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ" + "ହିପ୍‌ ଡମ୍ପ ଶେୟାର୍‍ କରିବେ?" + "%1$s ପ୍ରୋସେସ୍‍ ଏହାର ଧାର୍ଯ୍ୟ %2$s ମେମୋରୀ ସୀମାରୁ ବାହରକୁ ଗଲା। ଏହାର ଡେଭଲପରଙ୍କ ସହ ଶେୟାର୍‍ କରିବାକୁ ଏକ ହିପ୍‍ ଡମ୍ପ ଉପଲବ୍ଧ ଅଛି। ସାବଧାନ ରୁହନ୍ତୁ: ଆପ୍ଲିକେଶନର ଆକ୍ସେସ୍‍ ରହିଥିବା ଆପଣଙ୍କର ଯେକୌଣସି ବ୍ୟକ୍ତିଗତ ସୂଚନା ଏହି ହିପ୍‍ ଡମ୍ପରେ ରହିପାରେ।" + "ଟେକ୍ସଟ୍‍ ପାଇଁ ଏକ କାର୍ଯ୍ୟ ବାଛନ୍ତୁ" + "ରିଙ୍ଗର୍‌ ଭଲ୍ୟୁମ୍" + "ମିଡିଆ ଭଲ୍ୟୁମ୍‌" + "ବ୍ଲୁ-ଟୁଥ୍ ମାଧ୍ୟମରେ ଚାଲୁଛି" + "ରିଙ୍ଗଟୋନ୍‍‍କୁ ନିରବ ଭାବେ ସେଟ୍ କରାଯାଇଛି" + "ଇନ୍‍-କଲ୍‍ ଭଲ୍ୟୁମ୍‌" + "ବ୍ଲୁ-ଟୁଥ୍ ଇନ୍-କଲ୍ ଭଲ୍ୟୁମ୍‌" + "ଆଲାର୍ମର ଭଲ୍ୟୁମ୍‌" + "ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍‍" + "ଭଲ୍ୟୁମ୍" + "ବ୍ଲୁ-ଟୁଥ୍‍ ଭଲ୍ୟୁମ୍‍" + "ରିଙ୍ଗଟୋନ୍‌ ଭଲ୍ୟୁମ୍" + "କଲ୍‍ ଭଲ୍ୟୁମ୍‍" + "ମିଡିଆ ଭଲ୍ୟୁମ୍‍" + "ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍‍" + "ଡିଫଲ୍ଟ ରିଙ୍ଗଟୋନ୍‌" + "ଡିଫଲ୍ଟ (%1$s)" + "କିଛିନୁହେଁ" + "ରିଙ୍ଗଟୋନ୍‌" + "ଆଲାର୍ମ ଶବ୍ଦ" + "ବିଜ୍ଞପ୍ତି ଶବ୍ଦ" + "ଅଜଣା" + + ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କଗୁଡ଼ିକ ଉପଲବ୍ଧ + ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ଉପଲବ୍ଧ + + + ମୁକ୍ତ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କଗୁଡ଼ିକ ଉପଲବ୍ଧ + ମୁକ୍ତ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ଉପଲବ୍ଧ + + + + + + + + + + + + + + + + + + + + + + + + + + + + "ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍‍-ଇନ୍‍ କରନ୍ତୁ" + "ନେଟ୍‌ୱର୍କରେ ସାଇନ୍‍ ଇନ୍‍ କରନ୍ତୁ" + + + + + "ବିକଳ୍ପ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ" + "%1$sକୁ ବଦଳାଗଲା" + + + "%1$s ରୁ %2$sକୁ ବଦଳାଗଲା" + + "ଏକ ଅଜଣା ନେଟ୍‌ୱର୍କ ପ୍ରକାର" + "ୱାଇ-ଫାଇ ସହ ସଂଯୋଗ ହୋଇପାରିଲା ନାହିଁ" + " ଏହାର ଦୁର୍ବଳ ଇଣ୍ଟରନେଟ୍‍ ସଂଯୋଗ ରହିଛି।" + "ସଂଯୋଗର ଅନୁମତି ଦେବେ?" + "ଆପ୍ଲିକେଶନ୍‍ %1$s %2$s ୱାଇ-ଫାଇ ନେଟୱର୍କକୁ ସଂଯୋଗ କରିବାକୁ ଚାହେଁ" + "ଏକ ଅନୁପ୍ରୟୋଗ" + "ୱାଇ-ଫାଇ ଡାଇରେକ୍ଟ" + "ୱାଇ-ଫାଇ ଡାଇରେକ୍ଟ ଆରମ୍ଭ କରନ୍ତୁ। ଏହା ୱାଇ-ଫାଇ କ୍ଲାଏଣ୍ଟ/ହଟସ୍ପଟ୍‍କୁ ବନ୍ଦ କରିଦେବ।" + "ୱାଇ-ଫାଇ ଡାଇରେକ୍ଟ ଆରମ୍ଭ କରିପାରିଲା ନାହିଁ।" + "ୱାଇ-ଫାଇ ଡାଇରେକ୍ଟ ଅନ୍‍ ଅଛି" + "ସେଟିଙ୍ଗ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ" + "ସ୍ୱୀକାର କରନ୍ତୁ" + "ପ୍ରତ୍ୟାଖ୍ୟାନ" + "ନିମନ୍ତ୍ରଣ ପଠାଗଲା" + "ସଂଯୁକ୍ତ ହେବାକୁ ନିମନ୍ତ୍ରଣ" + "ପ୍ରେରକ:" + "ପ୍ରାପ୍ତେଷୁ:" + "ଆବଶ୍ୟକ PIN ଟାଇପ୍‍ କରନ୍ତୁ:" + "PIN:" + "%1$s ସହ ସଂଯୁକ୍ତ ହୋଇଥିବାବେଳେ, ୱାଇ-ଫାଇଠାରୁ ଟାବଲେଟ୍‍ଟି ଅସ୍ଥାୟୀ ଭାବେ ବିଚ୍ଛିନ୍ନ ହେବ" + "%1$s ସହ ସଂଯୋଗ ହେବାବେଳେ ଟିଭି ଅସ୍ଥାୟୀ ଭାବେ ୱାଇ-ଫାଇରୁ ବିଚ୍ଛିନ୍ନ ହେବ" + "%1$s ସହ ସଂଯୋଗ ଥିବାବେଳେ ଫୋନ୍‌ଟି ୱାଇ-ଫାଇରୁ ଅସ୍ଥାୟୀ ଭାବରେ ବିଚ୍ଛିନ୍ନ ହେବ" + "ବର୍ଣ୍ଣ ଲେଖନ୍ତୁ" + "SMS ମେସେଜ୍‌ଗୁଡ଼ିକୁ ପଠାଯାଉଛି" + "<b>%1$s</b> ବହୁତ ସଂଖ୍ୟାର SMS ମେସେଜ୍‍ ପଠାଉଛି। ଏହି ଆପ୍‍ ମେସେଜ୍‍ ପଠାଇବା ଜାରି ରଖିବାକୁ ଆପଣ ଅନୁମତି ଦେବେ କି?" + "ଅନୁମତି ଦିଅନ୍ତୁ" + "ପ୍ରତ୍ୟାଖ୍ୟାନ" + "<b>%1$s</b> <b>%2$s</b>କୁ ଏକ ମେସେଜ୍‍ ପଠାଇବାକୁ ଚାହେଁ।" + "ଏହା ଦ୍ୱାରା "" ଆପଣଙ୍କ ମୋବାଇଲ୍ ଆକାଉଣ୍ଟରୁ ପଇସା କଟିପାରେ। " + " ଆପଣଙ୍କ ମୋବାଇଲ୍ ଆକାଉଣ୍ଟରୁ ପଇସା କଟିପାରେ। " + "ପଠାନ୍ତୁ" + "କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ" + "ମୋ ପସନ୍ଦ ମନେରଖନ୍ତୁ" + "ଏହାକୁ ଆପଣ ସେଟିଙ୍ଗ ଓ ଆପ୍‍ରେ ପରବର୍ତ୍ତୀ ସମୟରେ ବଦଳାଇପାରିବେ" + "ସର୍ବଦା ଅନୁମତି ଦିଅନ୍ତୁ" + "ଆଦୌ ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ" + "SIM କାର୍ଡ ବାହାର କରିଦିଆଯାଇଛି" + "ଯେପର୍ଯ୍ୟନ୍ତ ଆପଣ କୌଣସି ବୈଧ SIM କାର୍ଡ ବ୍ୟବହାର କରି ରିଷ୍ଟାଟ୍ କରିନାହାନ୍ତି, ସେପର୍ଯ୍ୟନ୍ତ କୌଣସି ମୋବାଇଲ୍ ନେଟୱର୍କ ଉପଲବ୍ଧ ହେବନାହିଁ।" + "ହୋଇଗଲା" + "SIM କାର୍ଡ ଯୋଡ଼ାଯାଇଛି" + "ମୋବାଇଲ୍‍ ନେଟ୍‍ୱର୍କ ଆକ୍ସେସ୍‌ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ" + "ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ" + + + + + + + + + + + + + "ସମୟ ସେଟ୍ କରନ୍ତୁ" + "ତାରିଖ ସେଟ୍‍ କରନ୍ତୁ" + "ସେଟ୍‍ କରନ୍ତୁ" + "ହୋଇଗଲା" + "ନୂଆ: " + "%1$s ଦ୍ୱରା ପ୍ରଦତ୍ତ।" + "କୌଣସି ଅନୁମତିର ଆବଶ୍ୟକତା ନାହିଁ" + "ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ" + "ଠିକ୍‍ ଅଛି" + + + + + + + + + + + + + + + "ଅଧିକ ବିକଳ୍ପ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।" + + + + + + + "USB ଡିବଗିଙ୍ଗ ସଂଯୁକ୍ତ ହୋଇଛି" + "USB ଡିବଗିଙ୍ଗକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।" + "USB ଡିବଗିଙ୍ଗକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ।" + + + + + + + + + + + + + "କୀ’ବୋର୍ଡ ପରିବର୍ତ୍ତନ କରନ୍ତୁ" + "ଫିଜିକାଲ୍‌ କୀ’ବୋର୍ଡ ସକ୍ରିୟ ଥିବାବେଳେ ଏହାକୁ ସ୍କ୍ରୀନ୍‌ ଉପରେ ରଖନ୍ତୁ" + "ଭର୍ଚୁଆଲ୍ କୀ’ବୋର୍ଡ ଦେଖାନ୍ତୁ" + + + + + " ABCDEFGHIJKLMNOPQRSTUVWXYZ" + " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + + + + + + + + + + "%s ପ୍ରସ୍ତୁତ କରାଯାଉଛି" + "ତ୍ରୁଟି ପାଇଁ ଯାଞ୍ଚ କରାଯାଉଛି" + "ନୂଆ %s ଚିହ୍ନଟ ହେଲା" + "ଫଟୋ ଓ ମିଡିଆ ସ୍ଥାନାନ୍ତର କରାଯିବା ପାଇଁ" + "%s କରପ୍ଟ ହୋଇଯାଇଛି" + "%s କରପ୍ଟ ହୋଇଯାଇଛି। ଠିକ୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।" + + + "%s ସପୋର୍ଟ କରୁନାହିଁ" + "ଏହି ଡିଭାଇସ୍ ଏହି %sକୁ ସପୋର୍ଟ କରେନାହିଁ। ଗୋଟିଏ ସପୋର୍ଟ କରୁଥିବା ଫର୍ମାଟ୍‌ରେ ସେଟ୍‍ ଅପ୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।" + + + "%sକୁ ହଠାତ୍‌ କାଢ଼ିଦିଆଗଲା" + "ଡାଟା ନଷ୍ଟ ନହେବା ପାଇଁ ବାହାର କରିବା ପୂର୍ବରୁ %sକୁ ଅନ୍‌ମାଉଣ୍ଟ କରନ୍ତୁ" + "%s କାଢ଼ିଦିଆଗଲା" + "%s ବାହାର କରାଗଲା; ନୂଆ ଗୋଟିଏ ଭର୍ତ୍ତି କରନ୍ତୁ" + "%s ଏପର୍ଯ୍ୟନ୍ତ ବାହାର କରାଯାଉଛି…" + "କାଢ଼ନ୍ତୁ ନାହିଁ" + "ସେଟ୍ ଅପ୍ କରନ୍ତୁ" + "ବାହାର କରନ୍ତୁ" + "ଖୋଜନ୍ତୁ" + "%s ନାହିଁ" + "ଏହି ଡିଭାଇସ୍‍ ପୁଣି ଭର୍ତ୍ତି କରନ୍ତୁ" + "%s ନିଆଯାଉଛି" + "ଡାଟା ନିଆଯାଉଛି" + "ନେବା ସମ୍ପୂର୍ଣ୍ଣ ହେଲା" + "ଡାଟା %sକୁ ନିଆଗଲା" + "ଡାଟା ନିଆଯାଇପାରିଲା ନାହିଁ" + "ଡାଟାକୁ ମୂଳ ଲୋକେଶନରେ ଛାଡ଼ିଦିଆଗଲା" + "ବାହାର କରିଦିଆଗଲା" + "ବାହାର କରାଗଲା" + "ଯାଞ୍ଚ କରାଯାଉଛି…" + "ପ୍ରସ୍ତୁତ" + "କେବଳ-ପଢ଼ିବା ପାଇଁ" + "ଅସୁରକ୍ଷିତ ଭାବେ କାଢ଼ିଦିଆଗଲା" + "କରପ୍ଟ ହୋଇଯାଇଛି" + "ସପୋର୍ଟ କରୁନାହିଁ" + "ବାହାର କରାଯାଉଛି…" + "ଫର୍ମାଟ୍‍ କରାଯାଉଛି…" + "ଭର୍ତ୍ତି କରାଯାଇନାହିଁ" + "କୌଣସି ଗତିବିଧି ମେଳ ହେଉନାହିଁ।" + "ମିଡିଆ ଆଉଟପୁଟ୍‍କୁ ରୁଟ୍‍ କରନ୍ତୁ" + "ମିଡିଆ ଆଉଟ୍‍ପୁଟ୍‍କୁ ଅନ୍ୟ ଏକ୍ସଟର୍ନଲ୍‌ ଡିଭାଇସ୍‍ରେ ଚଲାଇବା ପାଇଁ ଆପ୍ଲିକେଶନ୍‌କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଇନଷ୍ଟଲ୍‍ ସେସନ୍‍ ପଢ଼ନ୍ତୁ" + "ଅନୁପ୍ରୟୋଗର ଇନଷ୍ଟଲ୍‍ ଅବଧିକୁ ପଢିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହି ସକ୍ରିୟ ପ୍ୟାକେଜ୍‌କୁ ଇନଷ୍ଟଲ୍‍ ବିଷୟରେ ବିବରଣୀ ଦେଖିବାକୁ ଅନୁମତି ଦେଇଥାଏ।" + "ପ୍ୟାକେଜ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବା ପାଇଁ ଅନୁରୋଧ କରନ୍ତୁ" + "ପ୍ୟାକେଜଗୁଡ଼ିକର ଇନଷ୍ଟଲେଶନ୍‍ ଅନୁରୋଧ କରିବା ପାଇଁ ଆପ୍ଲିକେଶନକୁ ଅନୁମତି ଦିଏ।" + + + + + "ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବା ପାଇଁ ପଚାରନ୍ତୁ" + "ଆପ୍‍ ପାଇଁ ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବାର ଅନୁମତି ମାଗିବା ନିମନ୍ତେ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।" + "ଜୁମ୍ ନିୟନ୍ତ୍ରଣ ପାଇଁ ଦୁଇଥର ଟାପ୍‌ କରନ୍ତୁ" + "ୱିଜେଟ୍‍ ଯୋଡ଼ିପାରିବ ନାହିଁ।" + "ଯାଆନ୍ତୁ" + "ସର୍ଚ୍ଚ କରନ୍ତୁ" + "ପଠାନ୍ତୁ" + "ପରବର୍ତ୍ତୀ" + "ହୋଇଗଲା" + "ପୂର୍ବବର୍ତ୍ତୀ" + "କାମ କରନ୍ତୁ" + "%sବ୍ୟବହାର କରି\n ଡାଏଲ୍ କରନ୍ତୁ" + "%sବ୍ୟବହାର କରି\n ଯୋଗାଯୋଗ ତିଆରି କରନ୍ତୁ" + "ବର୍ତ୍ତମାନ ଓ ଭବିଷ୍ୟତରେ ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ଆକ୍ସେସ୍‌ କରିବାକୁ ନିମ୍ନରୁ ଗୋଟିଏ କିମ୍ବା ଅଧିକ ଆପ୍‍ ଅନୁମତି ଅନୁରୋଧ କରନ୍ତି।" + "ଆପଣ ଏହି ଅନୁରୋଧକୁ ଅନୁମତି ଦେବାକୁ ଚାହାଁନ୍ତି କି?" + "ଆକ୍ସେସ୍‌ ଅନୁରୋଧ" + "ଅନୁମତି ଦିଅନ୍ତୁ" + "ପ୍ରତ୍ୟାଖ୍ୟାନ" + "ଅନୁମତି ଅନୁରୋଧ କରାଯାଇଛି" + "%s ଆକାଉଣ୍ଟ ପାଇଁ ଅନୁମତି\n ଅନୁରୋଧ କରାଯାଇଛି।" + "ଆପଣ ନିଜର ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ ବାହାରେ ଏହି ଆପ୍‌ର ପ୍ରୟୋଗ କରୁଛନ୍ତି" + "ଆପଣ ନିଜ ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ରେ ଏହି ଆପ୍‌ର ବ୍ୟବହାର କରୁଛନ୍ତି" + "ଇନପୁଟ୍ ପଦ୍ଧତି" + "ସିଙ୍କ୍" + "ଆକ୍ସେସିବିଲିଟୀ" + "ୱାଲପେପର୍" + "ୱାଲପେପର୍‍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ।" + "ବିଜ୍ଞପ୍ତି ଶ୍ରୋତା" + + + "ସର୍ତ୍ତ ପ୍ରଦାତା" + + + "VPN ସକ୍ରିୟ ହେଲା" + "%s ଦ୍ୱାରା VPN ସକ୍ରିୟ କରାଯାଇଛି" + "ନେଟ୍‌ୱର୍କକୁ ପରିଚାଳନା କରିବା ପାଇଁ ଟାପ୍‌ କରନ୍ତୁ।" + "%sରେ ସଂଯୋଗ କରାଯାଇଛି। ନେଟୱର୍କକୁ ପରିଚାଳନା କରିବାକୁ ଟାପ୍‌ କରନ୍ତୁ।" + "ସର୍ବଦା-ଅନ୍‍ VPNରେ ସଂଯୋଗ କରୁଛି…" + "ସର୍ବଦା-ଅନ୍‍ VPN ସଂଯୁକ୍ତ" + + + "ସର୍ବଦା-ଅନ୍‍ VPN ତ୍ରୁଟି" + + + "ଫାଇଲ୍ ଚୟନ କରନ୍ତୁ" + "କୌଣସି ଫାଇଲ୍ ଚୟନ କରାଯାଇନାହିଁ" + "ରିସେଟ୍‍ କରନ୍ତୁ" + "ଦାଖଲ କରନ୍ତୁ" + "କାର୍ ମୋଡ୍ ସକ୍ଷମ କରାଯାଇଛି" + "କାର୍‍ ମୋଡ୍‌ରୁ ବାହାରିଯିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ" + "ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି" + "ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।" + + + + + "ଫେରନ୍ତୁ" + "ପରବର୍ତ୍ତୀ" + "ଛାଡ଼ିଦିଅନ୍ତୁ" + "କୌଣସି ମେଳକ ନାହିଁ" + "ପୃଷ୍ଠାରେ ଖୋଜନ୍ତୁ" + + %dରୁ %d + 1ଟି ମେଳ + + "ହୋଇଗଲା" + "USB ଷ୍ଟୋରେଜ୍‌ ଲିଭାଯାଉଛି…" + "SD କାର୍ଡ ଲିଭାଯାଉଛି…" + "ଶେୟାର୍‍" + "ଖୋଜନ୍ତୁ" + "ୱେବ୍ ସର୍ଚ୍ଚ" + "ପରବର୍ତ୍ତୀ ଖୋଜନ୍ତୁ" + "ପୂର୍ବବର୍ତ୍ତୀ ଖୋଜନ୍ତୁ" + "%sଙ୍କଠାରୁ ଲୋକେଶନ୍ ଅନୁରୋଧ ଆସିଛି" + "ଲୋକେଶନ୍ ଅନୁରୋଧ" + "%1$s (%2$s)ଙ୍କ ଦ୍ୱାରା ଅନୁରୋଧ କରାଯାଇଛି" + "ହଁ" + "ନା" + "ଡିଲିଟ୍‌ କରିବାର ସୀମା ଅତିକ୍ରମ ହୋଇଯାଇଛି" + "%3$s ଆକାଉଣ୍ଟର %2$s ପାଇଁ %1$d ଟି ଡିଲିଟ୍‍ କରାଯାଇଥିବା ଆଇଟମ୍‍ ରହିଛି। ଆପଣ କ’ଣ କରିବାକୁ ଚାହାଁନ୍ତି?" + "ଆଇଟମ୍‍ ଡିଲିଟ୍‍ କରନ୍ତୁ" + "ଡିଲିଟ୍‍ଗୁଡ଼ିକୁ ପୂର୍ବାବସ୍ଥାକୁ ଫେରାଇ ଆଣନ୍ତୁ" + "ଏବେ କିଛି କରନ୍ତୁ ନାହିଁ" + "ଗୋଟିଏ ଆକାଉଣ୍ଟର ଚୟନ କରନ୍ତୁ" + "ଗୋଟିଏ ଆକାଉଣ୍ଟ ଯୋଡ଼ନ୍ତୁ" + "ଆକାଉଣ୍ଟ ଯୋଡ଼ନ୍ତୁ" + "ବଢ଼ାନ୍ତୁ" + "କମ୍‍ କରନ୍ତୁ" + "%s ସ୍ପର୍ଶ କରନ୍ତୁ ଏବଂ ଧରିରଖନ୍ତୁ" + "ବଢ଼ାଇବା ପାଇଁ ଉପରକୁ ଓ କମାଇବା ପାଇଁ ତଳକୁ ସ୍ଲାଇଡ୍‍ କରନ୍ତୁ।" + "ମିନିଟ୍‍ ବଢ଼ାନ୍ତୁ" + "ମିନିଟ୍‍ କମ୍‍ କରନ୍ତୁ" + "ଘଣ୍ଟା ବଢ଼ାନ୍ତୁ" + "ଘଣ୍ଟା କମ୍‍ କରନ୍ତୁ" + "PM ସେଟ୍‍ କରନ୍ତୁ" + "AM ସେଟ୍‍ କରନ୍ତୁ" + "ମାସ ବଢ଼ାନ୍ତୁ" + "ମାସ କମ୍‍ କରନ୍ତୁ" + "ଦିନ ବଢ଼ାନ୍ତୁ" + "ଦିନ କମ୍‍ କରନ୍ତୁ" + "ବର୍ଷ ବଢ଼ାନ୍ତୁ" + "ବର୍ଷ କମ୍‍ କରନ୍ତୁ" + "ପୂର୍ବ ମାସ" + "ପରବର୍ତ୍ତୀ ମାସ" + "ALT" + "କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ" + "ଡିଲିଟ୍‍ କରନ୍ତୁ" + "ହୋଇଗଲା" + "ମୋଡ୍‍ ପରିବର୍ତ୍ତନ" + "ଶିଫ୍ଟ" + "ଏଣ୍ଟର୍‌" + "ଗୋଟିଏ ଆପ୍‍ ବାଛନ୍ତୁ" + "%s ଲଞ୍ଚ କରାଯାଇପାରିଲା ନାହିଁ" + "ଏହାଙ୍କ ସହ ଶେୟାର୍‍ କରନ୍ତୁ" + "%s ସହ ଶେୟାର୍‍ କରନ୍ତୁ" + "ହ୍ୟାଣ୍ଡେଲ୍‍ ସ୍ଲାଇଡ୍‍ କରାଯାଉଛି। ସ୍ପର୍ଶ କରି ଧରିରଖନ୍ତୁ।" + "ଅନଲକ୍‍ କରିବାକୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ।" + "ହୋମ୍ ପେଜ୍‌କୁ ନେଭିଗେଟ୍ କରନ୍ତୁ" + "ଉପରକୁ ନେଭିଗେଟ୍ କରନ୍ତୁ" + "ଅଧିକ ବିକଳ୍ପ" + "%1$s, %2$s" + "%1$s, %2$s, %3$s" + + + "SD କାର୍ଡ" + "%s SD କାର୍ଡ" + "USB ଡ୍ରାଇଭ୍‍" + "%s USB ଡ୍ରାଇଭ୍‍" + "USB ଷ୍ଟୋରେଜ୍‌" + "ଏଡିଟ୍‌ କରନ୍ତୁ" + + + + + + + "ୱାଇ-ଫାଇ ଡାଟା ସୀମାରେ ପହଞ୍ଚିଗଲା" + + + + + + + + + "ବ୍ୟାକଗ୍ରାଉଣ୍ଡ ଡାଟା ପ୍ରତିବନ୍ଧିତ" + "ପ୍ରତିବନ୍ଧକ ବାହାର କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।" + + + + + + + "ସୁରକ୍ଷା ସର୍ଟିଫିକେଟ୍‍" + "ସର୍ଟିଫିକେଟ୍‍ ବୈଧ ନୁହେଁ।" + "ଏହାଙ୍କୁ ଜାରି କରାଯାଇଛି:" + "ସାଧାରଣ ନାମ:" + "ସଂସ୍ଥା:" + "ସଂଗଠନାତ୍ମକ ୟୁନିଟ୍:" + "ଜାରି କରିଛନ୍ତି:" + "ବୈଧତା:" + "ଜାରି କରାଯାଇଥିବା ତାରିଖ:" + "ସମାପ୍ତ ହେବାର ତାରିଖ ହେଉଛି:" + "କ୍ରମିକ ସଂଖ୍ୟା:" + "ଆଙ୍ଗୁଠି ଚିହ୍ନ:" + "SHA-256 ଆଙ୍ଗୁଠି ଚିହ୍ନ:" + "SHA-1 ଆଙ୍ଗୁଠି ଚିହ୍ନ:" + "ସମସ୍ତ ଦେଖନ୍ତୁ" + "ଗତିବିଧି ଚୟନ କରନ୍ତୁ" + "ଏହାଙ୍କ ସହ ଶେୟାର୍‍ କରନ୍ତୁ" + "ପଠାଯାଉଛି…" + "ବ୍ରାଉଜର୍‍ ଲଞ୍ଚ କରିବେ?" + "କଲ୍‍ ସ୍ୱୀକାର କରିବେ?" + "ସର୍ବଦା" + "ଥରେ ମାତ୍ର" + "%1$s ୱର୍କ ପ୍ରୋଫାଇଲ୍‌କୁ ସପୋର୍ଟ କରୁନାହିଁ" + "ଟାବଲେଟ୍‌" + "TV" + "ଫୋନ୍" + "ଡକ୍‌ ସ୍ପିକର୍‌" + + + "ହେଡଫୋନ୍‍" + + + "ସିଷ୍ଟମ୍‌" + "ବ୍ଲୁ-ଟୁଥ୍‌ ଅଡିଓ" + "ୱେୟାର୍‍ଲେସ୍‍ ଡିସ୍‍ପ୍ଲେ" + "କାଷ୍ଟ କରନ୍ତୁ" + "ଡିଭାଇସ୍‍ ସଂଯୋଗ କରନ୍ତୁ" + "ଡିଭାଇସ୍‍ରେ ସ୍କ୍ରୀନ୍‍ କାଷ୍ଟ କରନ୍ତୁ" + "ଡିଭାଇସ୍‍ ଖୋଜାଯାଉଛି…" + "ସେଟିଙ୍ଗ" + "ବିଛିନ୍ନ କରନ୍ତୁ" + "ସ୍କାନ୍‌ କରୁଛି…" + "ସଂଯୋଗ କରୁଛି..." + "ଉପଲବ୍ଧ" + "ଉପଲବ୍ଧ ନାହିଁ" + "ବ୍ୟବହାରରେ ଅଛି" + "ବିଲ୍ଟ-ଇନ୍‍ ସ୍କ୍ରୀନ୍‌" + "HDMI ସ୍କ୍ରୀନ୍‌" + "ପ୍ରାୟତଃ #%1$d" + "%1$s: %2$dx%3$d, %4$d dpi" + ", ସୁରକ୍ଷିତ" + "ପାଟର୍ନ ଭୁଲି ଯାଇଛନ୍ତି" + "ଭୁଲ ପାଟର୍ନ" + "ଭୁଲ ପାସ୍‌ୱର୍ଡ" + "ଭୁଲ PIN" + + "ନିଜ ପାଟର୍ନ ଆଙ୍କନ୍ତୁ" + "SIM PIN ଲେଖନ୍ତୁ" + "PIN ଲେଖନ୍ତୁ" + "ପାସ୍‌ୱର୍ଡ ଲେଖନ୍ତୁ" + "SIM ବର୍ତ୍ତମାନ ଅକ୍ଷମ ଅଟେ। ଜାରି ରଖିବାକୁ PUK କୋଡ୍‍ ଏଣ୍ଟର୍ କରନ୍ତୁ। ବିବରଣୀ ପାଇଁ ନିଜ କେରିଅର୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।" + "ନିଜ ଇଚ୍ଛାର PIN କୋଡ୍‍ ଲେଖନ୍ତୁ" + "ନିଜ ଇଚ୍ଛାର PIN କୋଡ୍‍ ନିଶ୍ଚିତ କରନ୍ତୁ" + "SIM କାର୍ଡ ଅନଲକ୍‍ କରାଯାଉଛି…" + "ଭୁଲ PIN କୋଡ୍।" + "4 ରୁ 8 ନମ୍ବର ବିଶିଷ୍ଟ ଏକ PIN ଟାଇପ୍ କରନ୍ତୁ।" + "PUK କୋଡ୍‍‍ରେ 8ଟି ନମ୍ବର ରହିଥାଏ।" + "ଠିକ୍‍ PUK କୋଡ୍‍ ପୁଣି ଲେଖନ୍ତୁ। ବାରମ୍ବାର ପ୍ରୟାସ କଲେ SIM କାର୍ଡ ସ୍ଥାୟୀ ରୂପେ ଅକ୍ଷମ ହୋଇଯିବ।" + "PIN କୋଡ୍‍ ମେଳ ଖାଉନାହିଁ" + "ଅନେକ ପାଟର୍ନ ପ୍ରୟାସ" + "ଅନଲକ୍‌ କରିବା ପାଇଁ, ଆପଣଙ୍କ Google ଆକାଉଣ୍ଟ ସହ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ।" + "ୟୁଜରନେମ୍‍ (ଇମେଲ୍)" + "ପାସ୍‌ୱର୍ଡ" + "ସାଇନ୍-ଇନ୍" + "ଅମାନ୍ୟ ୟୁଜରନେମ୍‍ କିମ୍ୱା ପାସ୍‌ୱର୍ଡ।" + "ଆପଣଙ୍କର ୟୁଜରନେମ୍‍ କିମ୍ୱା ପାସ୍‌ୱର୍ଡ ଭୁଲି ଯାଇଛନ୍ତି କି?\n""google.com/accounts/recovery"" ଭିଜିଟ୍‍ କରନ୍ତୁ।" + "ଆକାଉଣ୍ଟ ଯାଞ୍ଚ କରାଯାଉଛି…" + "ଆପଣଙ୍କ PINକୁ ଆପଣ %1$dଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। %2$d ସେକେଣ୍ଡ ପରେ \n\nପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣଙ୍କ ପାସ୍‌ୱର୍ଡକୁ ଆପଣ %1$dଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। %2$d ସେକେଣ୍ଡ ପରେ \n\nପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣଙ୍କ ଲକ୍‍ ଖୋଲିବା ପାଟର୍ନକୁ ଆପଣ %1$dଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। %2$d ସେକେଣ୍ଡ ପରେ \n\nପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଟାବଲେଟ୍‌ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଟାବଲେଟ୍‌ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ ଏବଂ ସମସ୍ତ ୟୁଜର୍‍ ଡାଟା ବାହାରିଯିବ।" + "ଟିଭିଟିକୁ ଅନଲକ୍‍ କରିବା ପାଇଁ ଆପଣ %1$d ଥର ଭୁଲ୍‍ ପ୍ରୟାସ କଲେ। ଆଉ %2$d ଟି ଭୁଲ୍‍ ପ୍ରୟାସ ପରେ, ଟିଭିଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ ଏବଂ ସମସ୍ତ ୟୁଜର୍‍ ଡାଟା ବାହାରିଯିବ।" + "ଫୋନ୍‌ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଫୋନ୍‌ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ ଏବଂ ସମସ୍ତ ୟୁଜର୍‍ ଡାଟା ବାହାରିଯିବ।" + "ଟାବଲେଟ୍‌ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଟାବଲେଟ୍‌ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ।" + "ଟିଭିକୁ ଅନଲକ୍‍ କରିବା ପାଇଁ ଆପଣ %d ଥର ଭୁଲ୍‍ ପ୍ରୟାସ କଲେ। ଟିଭିଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ।" + "ଫୋନ୍‌ ଅନଲକ୍‍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଫୋନ୍‌ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍‍ ହୋଇଯିବ।" + "ଆପଣଙ୍କ ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ ଏକ ଇମେଲ୍‍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ନିଜ ଟାବଲେଟ୍‌କୁ ଅନଲକ୍‌ କରିବା ପାଇଁ କୁହାଯିବ।\n\n%3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣଙ୍କ ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ୍‍ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ୍‍ ପ୍ରୟାସ ପରେ, ଏକ ଇମେଲ୍‍ ବ୍ୟବହାର କରି ଆପଣଙ୍କ ଟିଭିକୁ ଅନଲକ୍‍ କରିବାକୁ କୁହାଯିବ।\n\n%3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + "ଆପଣଙ୍କ ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ ଏକ ଇମେଲ୍‍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ନିଜ ଫୋନ୍‌କୁ ଅନଲକ୍‌ କରିବା ପାଇଁ କୁହାଯିବ।\n\n%3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + " — " + "ବାହାର କରନ୍ତୁ" + "ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିସ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।" + + + + + + + + + + + + + + + + + + + + + + + "ବର୍ତ୍ତମାନର ୟୁଜର୍‌ ହେଉଛନ୍ତି %1$s।" + "%1$s ରେ ସୁଇଚ୍ କରନ୍ତୁ…" + "%1$sଙ୍କୁ ଲଗଆଉଟ୍‍ କରାଯାଉଛି…" + "ମାଲିକ" + "ତ୍ରୁଟି" + + + "ଏହି କାର୍ଯ୍ୟକୁ ନିୟନ୍ତ୍ରଣ କରିବା ପାଇଁ କୌଣସି ଆପ୍ଲିକେଶନ୍‍ ମିଳିଲା ନାହିଁ" + "ବାହାର କରନ୍ତୁ" + "ISO A0" + "ISO A1" + "ISO A2" + "ISO A3" + "ISO A4" + "ISO A5" + "ISO A6" + "ISO A7" + "ISO A8" + "ISO A9" + "ISO A10" + "ISO B0" + "ISO B1" + "ISO B2" + "ISO B3" + "ISO B4" + "ISO B5" + "ISO B6" + "ISO B7" + "ISO B8" + "ISO B9" + "ISO B10" + "ISO C0" + "ISO C1" + "ISO C2" + "ISO C3" + "ISO C4" + "ISO C5" + "ISO C6" + "ISO C7" + "ISO C8" + "ISO C9" + "ISO C10" + "ଲେଟର୍‍" + "ସରକାରୀ ଲେଟର୍‌" + "ଲିଗାଲ" + "ଜୁନିଅର୍‍ ଲିଗାଲ୍‍" + "ଲେଜର୍‍" + "ଟାବଲୋଏଡ୍‍" + "ଇଣ୍ଡେକ୍ସ କାର୍ଡ 3x5" + "ଇଣ୍ଡେକ୍ସ କାର୍ଡ 4x6" + "ଇଣ୍ଡେକ୍ସ କାର୍ଡ 5x8" + "ମୋନାର୍କ" + "କ୍ୱାର୍ଟୋ" + "Foolscap" + "ROC 8K" + "ROC 16K" + "PRC 1" + "PRC 2" + "PRC 3" + "PRC 4" + "PRC 5" + "PRC 6" + "PRC 7" + "PRC 8" + "PRC 9" + "PRC 10" + "PRC 16K" + "Pa Kai" + "Dai Pa Kai" + "Jurro Ku Kai" + "JIS B10" + "JIS B9" + "JIS B8" + "JIS B7" + "JIS B6" + "JIS B5" + "JIS B4" + "JIS B3" + "JIS B2" + "JIS B1" + "JIS B0" + "JIS Exec" + "Chou4" + "Chou3" + "Chou2" + "Hagaki" + "Oufuku" + "Kahu" + "Kaku2" + "You4" + "ଅଜଣା ପୋର୍ଟ୍ରେଟ୍‍" + "ଅଜଣା ଲ୍ୟାଣ୍ଡସ୍କେପ୍‌" + "କ୍ୟାନ୍ସଲ୍‍ କରାଗଲା" + "କଣ୍ଟେଣ୍ଟ ଲେଖିବାବେଳେ ତ୍ରୁଟି" + "ଅଜଣା" + "ପ୍ରିଣ୍ଟ ସେବାକୁ ସକ୍ଷମ କରାଯାଇନାହିଁ" + "%s ସେବା ଇନଷ୍ଟଲ୍‍ କରାଯାଇସାରିଛି" + "ସକ୍ଷମ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ" + + + "PIN ଲେଖନ୍ତୁ" + "ଭୁଲ" + "ବର୍ତ୍ତମାନର PIN" + "ନୂଆ PIN" + "ନୂଆ PIN ନିଶ୍ଚିତ କରନ୍ତୁ" + "ପ୍ରତିବନ୍ଧକ ବଦଳାଇବା ପାଇଁ ଏକ PIN ତିଆରି କରନ୍ତୁ" + "PINଗୁଡିକ ମେଳ ହେଉନାହିଁ। ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।" + "PIN ବହୁତ ଛୋଟ। ଅତି କମ୍‍ରେ 4 ସଂଖ୍ୟା ବିଶିଷ୍ଟ ହେବା ଦରକାର।" + + %d ସେକେଣ୍ଡରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ + 1 ସେକେଣ୍ଡରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ + + "ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ" + "ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ଦେଖାଯାଉଛି" + "ବାହାରିବା ପାଇଁ, ଉପରୁ ତଳକୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ।" + "ବୁଝିଲି" + "ହୋଇଗଲା" + "ଘଣ୍ଟା ସର୍କୁଲାର୍‍ ସ୍ଲାଇଡର୍‍" + "ମିନିଟ୍ସ ସର୍କୁଲାର୍‍ ସ୍ଲାଇଡର୍‍" + "ଘଣ୍ଟା ଚୟନ କରନ୍ତୁ" + "ମିନିଟ୍‍ ଚୟନ କରନ୍ତୁ" + "ମାସ ଓ ଦିନ ଚୟନ କରନ୍ତୁ" + "ବର୍ଷ ଚୟନ କରନ୍ତୁ" + "%1$s ଡିଲିଟ୍‍ ହୋଇଗଲା" + "କାର୍ଯ୍ୟ %1$s" + "2ୟ କାର୍ଯ୍ୟ %1$s" + "3ୟ କାର୍ଯ୍ୟ %1$s" + "ଅନପିନ୍‌ କରିବା ପୂର୍ବରୁ PIN ପଚାରନ୍ତୁ" + "ଅନପିନ୍‌ କରିବା ପୂର୍ବରୁ ଲକ୍‌ ଖୋଲିବା ପାଟର୍ନ ପଚାରନ୍ତୁ" + "ଅନପିନ୍‌ କରିବା ପୂର୍ବରୁ ପାସ୍‌ୱର୍ଡ ପଚାରନ୍ତୁ" + + + + + + + + + + + "ଡାଟା ସେଭର୍‌ ଅନ୍ କରିବେ?" + "ଅନ୍ କରନ୍ତୁ" + + %1$d ମିନିଟ୍‍ ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + ଏକ ମିନିଟ୍‍ ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + + + %1$d ମିନିଟ୍ ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + 1 ମିନିଟ୍ ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + + + %1$d ଘଣ୍ଟା ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + ଏକ ଘଣ୍ଟା ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + + + %1$d ଘଣ୍ଟା ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + 1 ଘଣ୍ଟା ପାଇଁ (%2$s ପର୍ଯ୍ୟନ୍ତ) + + + %d ମିନିଟ୍‍ ପାଇଁ + ଏକ ମିନିଟ୍‍ ପାଇଁ + + + %d ମିନିଟ୍ ପାଇଁ + 1 ମିନିଟ୍ ପାଇଁ + + + %d ଘଣ୍ଟା ପାଇଁ + ଏକ ଘଣ୍ଟା ପାଇଁ + + + %d ଘଣ୍ଟା ପାଇଁ + 1 ଘଣ୍ଟା ପାଇଁ + + "%1$s ପର୍ଯ୍ୟନ୍ତ" + "%1$s (ପରବର୍ତ୍ତୀ ଆଲାର୍ମ) ପର୍ଯ୍ୟନ୍ତ" + + + "ଆପଣ \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅଫ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ" + "%1$s / %2$s" + "ଛୋଟ କରନ୍ତୁ" + "ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ" + "ବନ୍ଦ ରହିବାର ସମୟ" + "ସପ୍ତାହରାତି" + "ସପ୍ତାହାନ୍ତ" + "ଇଭେଣ୍ଟ" + + + "%1$s ଦ୍ୱାରା ନିଶବ୍ଦ କରନ୍ତୁ" + "ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ଏକ ସମସ୍ୟା ରହିଛି ଏବଂ ଆପଣ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଅସ୍ଥିର ରହିପାରେ।" + "ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ଏକ ସମସ୍ୟା ରହିଛି। ବିବରଣୀ ପାଇଁ ଆପଣଙ୍କ ଉତ୍ପାଦକଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।" + "USSD ଅନୁରୋଧ DIAL ଅନୁରୋଧକୁ ସଂଶୋଧନ କରାଗଲା।" + "USSD ଅନୁରୋଧ SS ଅନୁରୋଧକୁ ସଂଶୋଧନ କରାଗଲା।" + "USSD ଅନୁରୋଧ ନୂଆ USSD ଅନୁରୋଧକୁ ସଂଶୋଧନ କରାଗଲା।" + + + "SS ଅନୁରୋଧ DIAL ଅନୁରୋଧକୁ ସଂଶୋଧନ କରାଗଲା।" + + + "SS ଅନୁରୋଧ USSD ଅନୁରୋଧକୁ ସଂଶୋଧନ କରାଗଲା।" + "SS ଅନୁରୋଧ ନୂତନ SS ଅନୁରୋଧକୁ ସଂଶୋଧନ କରାଗଲା।" + "ୱର୍କ ପ୍ରୋଫାଇଲ୍‌" + + + + + "ଟୋଗଲ୍‍ ସମ୍ପ୍ରସାରଣ" + "Android USB ପେରିଫେରିଆଲ୍‍ ପୋର୍ଟ" + "Android" + "USB ପେରିଫେରିଆଲ୍‍ ପୋର୍ଟ" + "ଅଧିକ ବିକଳ୍ପ" + "ଓଭରଫ୍ଲୋ ବନ୍ଦ କରନ୍ତୁ" + "ବଡ଼ କରନ୍ତୁ" + "ବନ୍ଦ କରନ୍ତୁ" + "%1$s: %2$s" + + %1$d ଚୟନିତ + %1$d ଚୟନିତ + + + + + + "ସମ୍ପୃକ୍ତ ଲୋକଙ୍କ କାରଣରୁ ଏହା ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଅଟେ।" + + + + + + + "ପସନ୍ଦର ଅଞ୍ଚଳ" + "ଭାଷାର ନାମ ଟାଇପ୍‍ କରନ୍ତୁ" + "ପରାମର୍ଶିତ" + "ସମସ୍ତ ଭାଷା" + "ସମସ୍ତ ଅଞ୍ଚଳ" + "ସର୍ଚ୍ଚ କରନ୍ତୁ" + + + + + "ଅନ୍ କରନ୍ତୁ" + + + + + "ଆପଣଙ୍କ ପାଖରେ ନୂଆ ମେସେଜ୍‍ ରହିଛି" + "ଦେଖିବା ପାଇଁ SMS ଆପ୍‍ ଖୋଲନ୍ତୁ" + + + + + + + + + + + + + + + "ପିନ୍‍" + + + "ଆପ୍‍ ସୂଚନା" + + + "ଡେମୋ ଆରମ୍ଭ କରାଯାଉଛି…" + "ଡିଭାଇସ୍‍କୁ ରିସେଟ୍‍ କରାଯାଉଛି…" + "ଅକ୍ଷମ ହୋଇଛି %1$s" + "କନ୍‌ଫରେନ୍ସ କଲ୍‍" + "ଟୁଲଟିପ୍‍" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 45512a567b73fe7b2863bf05d5da7cca434d28ec..5ab8d3380095124ac322b47e76296c6d17b1862f 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -181,7 +181,7 @@ "O dispositivo é gerenciado" "Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes." "Seu dispositivo será limpo" - "Não é possível usar o app para administrador. Seu dispositivo passará por uma limpeza agora.\n\nEm caso de dúvidas, entre em contato com o administrador da sua organização." + "Não é possível usar o aplicativo para administrador. Seu dispositivo passará por uma limpeza agora.\n\nEm caso de dúvidas, entre em contato com o administrador da sua organização." "Impressão desativada por %s." "Eu" "Opções do tablet" @@ -236,6 +236,9 @@ "Modo avião" "Modo avião ATIVADO" "Modo avião DESATIVADO" + "Economia de bateria" + "A Economia de bateria está DESATIVADA" + "A Economia de bateria está ATIVADA" "Configurações" "Assistência" "Ajuda de voz" @@ -305,7 +308,7 @@ "Fazer gestos" "Toque, deslize, faça gestos de pinça e faça outros gestos." "Gestos de impressão digital" - "Pode captar gestos realizados no sensor de impressão digital do dispositivo." + "Pode captar gestos realizados no sensor de impressão digital do dispositivo." "desativar ou modificar a barra de status" "Permite que o app desative a barra de status ou adicione e remova ícones do sistema." "ser a barra de status" @@ -356,6 +359,8 @@ "Permite que o app torne partes de si mesmo persistentes na memória. Pode limitar a memória disponível para outros apps, deixando o tablet mais lento." "Permite que o app torne partes de si mesmo persistentes na memória. Isso pode limitar a memória disponível para outros apps, deixando a TV mais lenta." "Permite que o app torne partes de si mesmo persistentes na memória. Pode limitar a memória disponível para outros apps, deixando o telefone mais lento." + "executar serviço em primeiro plano" + "Permite que o app use serviços em primeiro plano." "medir o espaço de armazenamento do app" "Permite que o app recupere o código, os dados e os tamanhos de cache" "modificar configurações do sistema" @@ -802,6 +807,8 @@ "Desbloqueio com padrão." "Desbloqueio facial." "Desbloqueio com PIN." + "Desbloqueio com PIN do SIM." + "Desbloqueio com PUK do SIM." "Desbloqueio com senha." "Área do padrão." "Área de deslize." @@ -1074,29 +1081,33 @@ "Verificar atualizações" "O app %1$s, processo %2$s, violou a política StrictMode imposta automaticamente." "O processo %1$s violou a política StrictMode imposta automaticamente." - "O Android está sendo atualizado..." - "O Android está iniciando..." + + + + + + + + + + + + "Otimizando o armazenamento." - "Concluindo atualização do Android…" - "Alguns apps podem não funcionar corretamente até que a atualização seja concluída" + + "%1$s está fazendo upgrade…" "Otimizando app %1$d de %2$d." "Preparando %1$s." "Iniciando apps." "Concluindo a inicialização." "%1$s em execução" - - - - - - - - - - - - + "Toque para voltar ao jogo" + "Escolha o jogo" + "Para ter um melhor desempenho, apenas um desses jogos pode ser aberto por vez." + "Voltar para o app %1$s" + "Abrir %1$s" + "O app %1$s será fechado sem salvar" "%1$s excedeu o limite de memória" "O despejo de heap foi coletado. Toque para compartilhar" "Compartilhar despejo de heap?" @@ -1138,7 +1149,8 @@ "Não foi possível conectar-se à rede Wi‑Fi" "Toque para ver todas as redes" "Conectar" - "Todas as redes" + + "O Wi‑Fi será ativado automaticamente" "Quando você estiver perto de uma rede salva de alta qualidade" "Não ativar novamente" @@ -1204,6 +1216,7 @@ "Reiniciar" "Ativar serviço móvel" "Faça o download do app da operadora para ativar seu novo SIM" + "Faça o download do app %1$s para ativar seu novo SIM" "Fazer download do app" "Novo SIM inserido" "Toque para configurar" @@ -1247,7 +1260,7 @@ "%s exibido sobre outros apps" "%s exibido sobre outros apps." "Se você não deseja que o %s use este recurso, toque para abrir as configurações e desativá-lo." - "DESATIVAR" + "Desativar" "Preparando %s" "Procurando erros" "Novo %s detectado" @@ -1418,22 +1431,19 @@ "Drive USB %s" "Armazenamento USB" "Editar" - "Alerta de uso de dados" - "Toque para ver uso e config." - "Limite de dados 2G-3G atingido" - "Limite de dados 4G atingido" + "Aviso de dados" + "Você usou %s de dados" "Limite de dados móveis atingido" "Limite de dados Wi-Fi atingido" - "Dados pausados no resto do ciclo" - "Limite de dados 2G-3G excedido" - "Limite de dados 4G excedido" - "Limite de dados do celular excedido" - "Limite de dados Wi-Fi excedido" - "%s acima do limite especificado." + "Uso de dados pausado pelo restante do seu ciclo" + "Acima do limite de dados móveis" + "Acima do limite de dados Wi-Fi" + "Você ultrapassou %s do limite definido" "Dados de segundo plano restritos" "Toque para remover a restrição." - "Uso de dados excessivo" - "Seu uso de dados nos últimos dias é maior que o normal. Toque para ver o uso e as configurações." + "Alto uso de dados móveis" + "Seus apps usaram mais dados do que o normal" + "O app %s usou mais dados do que o normal" "Certificado de segurança" "Este certificado é válido." "Emitido para:" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 96a83f837fb3956e01e3c8ce9c3e09b14139c708..021e88ea6093d4176f2ecad475d574a6facd925b 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1019,6 +1019,9 @@ + + + @@ -1111,6 +1114,9 @@ + + + @@ -2113,6 +2119,45 @@ Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} on the decor view. --> + + + + + + + + + + @@ -4472,6 +4517,8 @@ + + @@ -4561,6 +4608,8 @@ + + @@ -5908,6 +5957,11 @@ + + diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index cfb5784666b906870b8ab1ea51728b82e08db427..c4fa190b228ddb4b65886448ddccba69c3d82e91 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2348,6 +2348,16 @@ + + - #ffff6d00 - #ff000000 + #ff1A73E8 + #ffff6d00 #ff22f033 diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ea8e4e78b1abf27975951717cf7d52e2fa3d3525..b9468dcfd67a7b6aff44f168b10ad47f96ba0e59 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1636,6 +1636,9 @@ 0 + + 5 + false @@ -2160,6 +2163,9 @@ false + + false + false @@ -2390,6 +2396,7 @@ com.android.server.notification.NotificationIntrusivenessExtractor com.android.server.notification.VisibilityExtractor + com.android.server.notification.BadgeExtractor @@ -2507,9 +2514,8 @@ restart lockdown logout - screenshot bugreport - users + screenshot @dimen/control_corner_material + + @dimen/progress_bar_corner_material true @@ -3356,4 +3364,6 @@ "wifi" + + com.android.managedprovisioning diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 2ce08ebb882f7e37a100a5c82f9c863e0e7a8bfc..7ff96fa7def264373156c085d9de315a8692ee69 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -32,7 +32,11 @@ 24dp - 24dp + @dimen/status_bar_height_portrait + + 24dp + + @dimen/status_bar_height_portrait 48dp @@ -541,7 +545,7 @@ 100dp 48dp - 2dp + 4dp 42dp 1.25 diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml index e3fdcece4ff1fa99a094dfaf4206dcade266a494..210f30eeb9025a77dc6eb6fb0f259a33a56a86c0 100644 --- a/core/res/res/values/dimens_material.xml +++ b/core/res/res/values/dimens_material.xml @@ -135,6 +135,7 @@ 2dp 4dp + 0dp + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5c9f86396772f5caaf1dd8252f197169ad8b328f..a5bd179826b14683f608c53c799668d9a6b80260 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -379,7 +379,7 @@ The admin app can\'t be used. Your device will now be - erased.\n\nIf you have questions, contact your organization's admin. + erased.\n\nIf you have questions, contact your organization\'s admin. Printing disabled by %s. @@ -539,6 +539,15 @@ Airplane mode is OFF + + Battery saver + + + Battery saver is OFF + + + Battery saver is ON + Settings @@ -764,7 +773,7 @@ Fingerprint gestures Can capture gestures performed on - the device's fingerprint sensor. + the device\'s fingerprint sensor. @@ -917,6 +926,11 @@ Allows the app to make parts of itself persistent in memory. This can limit memory available to other apps slowing down the TV. Allows the app to make parts of itself persistent in memory. This can limit memory available to other apps slowing down the phone. + + run foreground service + + Allows the app to make use of foreground services. + measure app storage space @@ -2923,19 +2937,25 @@ The process %1$s has has violated its self-enforced StrictMode policy. - - Android is upgrading\u2026 + + Phone is updating\u2026 + + Tablet is updating\u2026 + + Device is updating\u2026 - Android is starting\u2026 + Phone is starting\u2026 + + Tablet is starting\u2026 + + Device is starting\u2026 Optimizing storage. - - Finishing Android update\u2026 - - Some apps may not work properly until the upgrade finishes + + Finishing system update\u2026 %1$s is upgrading\u2026 @@ -3068,7 +3088,7 @@ Connect - All Networks + All networks Wi\u2011Fi will turn on automatically @@ -3320,8 +3340,7 @@ [CHAR LIMIT=NONE] --> If you don’t want %s to use this feature, tap to open settings and turn it off. - TURN OFF - + Turn off @@ -3775,7 +3794,7 @@ Data warning - You've used %s of data + You\'ve used %s of data Mobile data limit reached @@ -3789,7 +3808,7 @@ Over your Wi-Fi data limit - You've gone %s over your set limit + You\'ve gone %s over your set limit Background data restricted @@ -4864,7 +4883,7 @@ System changes - Do Not Disturb has changed + Do Not Disturb is hiding notifications to help you focus - Tap to check your behavior settings for interruptions + This is a new feature from the latest system update. Tap to change. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b705e5d420e1d84346b291423cdd52057da61bfa..f04f1caefa1063d0b2ae638909f42a99d40127e2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -218,6 +218,10 @@ + + + + @@ -315,6 +319,7 @@ + @@ -405,6 +410,7 @@ + @@ -1372,6 +1378,7 @@ + @@ -1388,6 +1395,9 @@ + + + @@ -1411,6 +1421,7 @@ + @@ -1729,6 +1740,9 @@ + + + @@ -2444,6 +2458,7 @@ + @@ -3237,6 +3252,9 @@ + + + @@ -3274,4 +3292,6 @@ + + diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 39310a84a2f70be4cffa1b38934aa8ac65156995..fdbcd8a27f912d615f1894d4e156fab7898983fb 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -126,6 +126,8 @@ easier. @style/Widget.DeviceDefault.ProgressBar.Inverse @style/Widget.DeviceDefault.ProgressBar.Small.Inverse @style/Widget.DeviceDefault.ProgressBar.Large.Inverse + @dimen/config_progressBarCornerRadius + @color/config_progress_background_tint @style/Widget.DeviceDefault.SeekBar @style/Widget.DeviceDefault.RatingBar @style/Widget.DeviceDefault.RatingBar.Indicator @@ -227,6 +229,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_dialogCornerRadius @style/Theme.DeviceDefault.Dialog.Alert + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -358,6 +384,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -414,6 +448,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -454,6 +496,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -689,6 +767,8 @@ easier. @style/Widget.DeviceDefault.Light.ProgressBar.Inverse @style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse @style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse + @dimen/config_progressBarCornerRadius + @color/config_progress_background_tint @style/Widget.DeviceDefault.Light.SeekBar @style/Widget.DeviceDefault.Light.RatingBar @style/Widget.DeviceDefault.Light.RatingBar.Indicator @@ -785,6 +865,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -804,6 +888,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -933,6 +1041,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -999,6 +1115,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -1039,6 +1163,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -1147,6 +1291,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius @@ -1195,6 +1351,10 @@ easier. @dimen/config_buttonCornerRadius @style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog + + + @color/config_progress_background_tint + @dimen/config_progressBarCornerRadius diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 6ae0ef3da2251a5efaa273a66a01bb1fdd92c853..9b633fc94a8f3130993346a342790f79f2ffe539 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -259,6 +259,7 @@ please see themes_device_defaults.xml. @style/Widget.Material.ProgressBar.Inverse @style/Widget.Material.ProgressBar.Small.Inverse @style/Widget.Material.ProgressBar.Large.Inverse + @dimen/progress_bar_corner_material @style/Widget.Material.SeekBar @style/Widget.Material.RatingBar @style/Widget.Material.RatingBar.Indicator @@ -402,6 +403,7 @@ please see themes_device_defaults.xml. @color/ripple_material_dark @color/btn_default_material_dark @color/switch_thumb_material_dark + ?attr/colorControlNormal @color/foreground_material_light @@ -631,6 +633,7 @@ please see themes_device_defaults.xml. @style/Widget.Material.Light.ProgressBar.Inverse @style/Widget.Material.Light.ProgressBar.Small.Inverse @style/Widget.Material.Light.ProgressBar.Large.Inverse + @dimen/progress_bar_corner_material @style/Widget.Material.Light.SeekBar @style/Widget.Material.Light.RatingBar @style/Widget.Material.Light.RatingBar.Indicator @@ -775,6 +778,7 @@ please see themes_device_defaults.xml. @color/ripple_material_light @color/btn_default_material_light @color/switch_thumb_material_light + ?attr/colorControlNormal @color/foreground_material_dark @@ -835,6 +839,7 @@ please see themes_device_defaults.xml. @color/ripple_material_light @color/btn_default_material_light @color/switch_thumb_material_light + ?attr/colorControlNormal - - + + + diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index d80c6972db1c2bc97521f253b900def1720a043b..899d630a51b7210945ec43c8864c1964823812b5 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -27,16 +27,16 @@ are totally dependent on the platform and can vary significantly, so should be measured on the shipping platform with a power meter. --> - 0 - 0.1 - 0.1 + 0.1 + 0.1 + 0.1 0.1 0.1 0.1 0.1 0.1 - 0.1 - 0.1 + 0.1 + 0.1 0.1 0.1 0.1 diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml index cdb71343e5e50082ab97b56b0db10151d129965a..001cc68d60c5d81c4d7ee8f80153d76d170799f0 100644 --- a/core/res/res/xml/power_profile_test.xml +++ b/core/res/res/xml/power_profile_test.xml @@ -24,9 +24,6 @@ sample values, not meant to reflect any real device. --> - - 0 - 3000 @@ -80,6 +77,8 @@ 60 + + 0.5 100 600 - - 100 + + 100.0 + + + 150.0 10 diff --git a/core/tests/BTtraffic/Android.mk b/core/tests/BTtraffic/Android.mk index 7d8352717a34022c759d527c0592cb9b8fbda3dd..f826ae9d79a189a2d9bf878c969735e3a11ec17f 100644 --- a/core/tests/BTtraffic/Android.mk +++ b/core/tests/BTtraffic/Android.mk @@ -9,6 +9,7 @@ LOCAL_RESOURCE_DIR := \ $(LOCAL_PATH)/res LOCAL_PACKAGE_NAME := bttraffic +LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk index 8df3827935aa040fee3f2105e69ab8beda9e052a..24f0cf07f44301bc15613b521b510abf148c090c 100644 --- a/core/tests/BroadcastRadioTests/Android.mk +++ b/core/tests/BroadcastRadioTests/Android.mk @@ -23,6 +23,7 @@ LOCAL_CERTIFICATE := platform LOCAL_MODULE_TAGS := tests # TODO(b/13282254): uncomment when b/13282254 is fixed # LOCAL_SDK_VERSION := current +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test testng diff --git a/core/tests/ConnectivityManagerTest/Android.mk b/core/tests/ConnectivityManagerTest/Android.mk index 5ed93f317b3f3059590f79995c6611b46af014c7..8c0a330e091cd659160bebf29e0762feb356a29b 100644 --- a/core/tests/ConnectivityManagerTest/Android.mk +++ b/core/tests/ConnectivityManagerTest/Android.mk @@ -25,6 +25,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := ConnectivityManagerTest +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform diff --git a/core/tests/SvcMonitor/Android.mk b/core/tests/SvcMonitor/Android.mk index 2b8045506f02e9ae6871b2b4724d39a9c2d82c44..94ddccbb71c0d96b1e783f0edeea94a80cbf5528 100644 --- a/core/tests/SvcMonitor/Android.mk +++ b/core/tests/SvcMonitor/Android.mk @@ -9,6 +9,7 @@ LOCAL_RESOURCE_DIR := \ $(LOCAL_PATH)/res LOCAL_PACKAGE_NAME := svcmonitor +LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk index ff9a0fe60f346615edc16c608d93959173e089ac..dc80d00e42c14387f59a0d96f4e1737672151516 100644 --- a/core/tests/bandwidthtests/Android.mk +++ b/core/tests/bandwidthtests/Android.mk @@ -29,6 +29,7 @@ LOCAL_JAVA_LIBRARIES := \ LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := BandwidthTests +LOCAL_PRIVATE_PLATFORM_APIS := true include $(BUILD_PACKAGE) diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk index 744e5b04974ec2707671a70075319c8c219cad55..bb4e302b75c5a34791acc8993b167720cad9ef96 100644 --- a/core/tests/bluetoothtests/Android.mk +++ b/core/tests/bluetoothtests/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := \ LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := BluetoothTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 2ea1b46ad208c336a4bc032a3b4bb35c81bee699..2d25c7811b40505b3c89dcec5728741a8cc496b6 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -56,6 +56,7 @@ LOCAL_JAVA_LIBRARIES := \ framework-atb-backward-compatibility \ LOCAL_PACKAGE_NAME := FrameworksCoreTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 7d5c60aa292b2c2429e92e6c10254b79f658918d..53c22f624e8cb3d2e8bb81a61a1fc9491324c450 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -51,6 +51,7 @@ + diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk index e31d50f15d5f877992d9ef92d25dfdb30a99a0c8..c3af6bd123b8566b976d0d261f3ad1f2a4ae886a 100644 --- a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk +++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk @@ -21,6 +21,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp +LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk index a63cf0e8e8b1400c1617c5bb4922a878cfe3726d..34016ed633b1bccb18271c5d3568b14795de4c59 100644 --- a/core/tests/coretests/BinderProxyCountingTestService/Android.mk +++ b/core/tests/coretests/BinderProxyCountingTestService/Android.mk @@ -21,6 +21,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := BinderProxyCountingTestService +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk index 628025751d77dbe557377110c9dc032e12e67e44..e04536ba749934518e1cb00fae46c47f869db1dd 100644 --- a/core/tests/coretests/BstatsTestApp/Android.mk +++ b/core/tests/coretests/BstatsTestApp/Android.mk @@ -25,6 +25,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := BstatsTestApp +LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform LOCAL_DEX_PREOPT := false LOCAL_PROGUARD_ENABLED := disabled diff --git a/core/tests/coretests/DisabledTestApp/Android.mk b/core/tests/coretests/DisabledTestApp/Android.mk index a5daedf06b8d69ac20453679d2603c56edeeaa42..e4304f7cef735e9a5febe301d26410f08d59d5e6 100644 --- a/core/tests/coretests/DisabledTestApp/Android.mk +++ b/core/tests/coretests/DisabledTestApp/Android.mk @@ -6,6 +6,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := DisabledTestApp +LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/EnabledTestApp/Android.mk b/core/tests/coretests/EnabledTestApp/Android.mk index 4b986d39b2097bc08581041d7e8ba068cc2bf9ed..cd37f0883c631f6f5667b23c2be28f8f1b48abfe 100644 --- a/core/tests/coretests/EnabledTestApp/Android.mk +++ b/core/tests/coretests/EnabledTestApp/Android.mk @@ -6,6 +6,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := EnabledTestApp +LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk index 1e032703e8b29885c89435d27011ecd45bf56eda..8a7d72a5200b5e2f675c170f318894c323871252 100644 --- a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk +++ b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk @@ -6,6 +6,7 @@ LOCAL_DEX_PREOPT := false # Make sure every package name gets the FrameworkCoreTests_ prefix. LOCAL_PACKAGE_NAME := FrameworkCoreTests_$(LOCAL_PACKAGE_NAME) +LOCAL_SDK_VERSION := current # Every package should have a native library LOCAL_JNI_SHARED_LIBRARIES := libframeworks_coretests_jni diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml index 7a90197c5b6639eca4058ce0d056f27403133530..ef3a481ae26cdbcfa7b26cb9d44184823f932d69 100644 --- a/core/tests/coretests/res/values/styles.xml +++ b/core/tests/coretests/res/values/styles.xml @@ -19,4 +19,16 @@ @null @null + + + + + diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index e575650393f0ee3b90f0fd3e202a5f9ec6de9664..3eefc362ab8654e75840ed3c93ace27a70660105 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -24,6 +24,9 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; import static android.app.servertransaction.ActivityLifecycleItem.ON_START; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; +import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; + +import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertArrayEquals; import static org.mockito.ArgumentMatchers.any; @@ -48,6 +51,10 @@ import org.junit.runner.RunWith; import org.mockito.InOrder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; /** Test {@link TransactionExecutor} logic. */ @RunWith(AndroidJUnit4.class) @@ -56,6 +63,7 @@ import java.util.ArrayList; public class TransactionExecutorTests { private TransactionExecutor mExecutor; + private TransactionExecutorHelper mExecutorHelper; private ClientTransactionHandler mTransactionHandler; private ActivityClientRecord mClientRecord; @@ -67,6 +75,7 @@ public class TransactionExecutorTests { when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord); mExecutor = spy(new TransactionExecutor(mTransactionHandler)); + mExecutorHelper = new TransactionExecutorHelper(); } @Test @@ -166,10 +175,42 @@ public class TransactionExecutorTests { pathExcludeLast(ON_DESTROY)); } + @Test(expected = IllegalArgumentException.class) + public void testLifecycleUndefinedStartState() { + mClientRecord.setState(UNDEFINED); + path(ON_CREATE); + } + + @Test(expected = IllegalArgumentException.class) + public void testLifecycleUndefinedFinishState() { + mClientRecord.setState(PRE_ON_CREATE); + path(UNDEFINED); + } + + @Test(expected = IllegalArgumentException.class) + public void testLifecycleInvalidPreOnCreateFinishState() { + mClientRecord.setState(ON_CREATE); + path(PRE_ON_CREATE); + } + + @Test(expected = IllegalArgumentException.class) + public void testLifecycleInvalidOnRestartStartState() { + mClientRecord.setState(ON_RESTART); + path(ON_RESUME); + } + + @Test(expected = IllegalArgumentException.class) + public void testLifecycleInvalidOnRestartFinishState() { + mClientRecord.setState(ON_CREATE); + path(ON_RESTART); + } + @Test public void testTransactionResolution() { ClientTransactionItem callback1 = mock(ClientTransactionItem.class); + when(callback1.getPostExecutionState()).thenReturn(UNDEFINED); ClientTransactionItem callback2 = mock(ClientTransactionItem.class); + when(callback2.getPostExecutionState()).thenReturn(UNDEFINED); ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); IBinder token = mock(IBinder.class); @@ -189,7 +230,7 @@ public class TransactionExecutorTests { } @Test - public void testRequiredStateResolution() { + public void testActivityResultRequiredStateResolution() { ActivityResultItem activityResultItem = ActivityResultItem.obtain(new ArrayList<>()); IBinder token = mock(IBinder.class); @@ -197,20 +238,161 @@ public class TransactionExecutorTests { token /* activityToken */); transaction.addCallback(activityResultItem); + // Verify resolution that should get to onPause + mClientRecord.setState(ON_RESUME); mExecutor.executeCallbacks(transaction); - verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE)); + + // Verify resolution that should get to onStart + mClientRecord.setState(ON_STOP); + mExecutor.executeCallbacks(transaction); + verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START)); + } + + @Test + public void testClosestStateResolutionForSameState() { + final int[] allStates = new int[] { + ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}; + + mClientRecord.setState(ON_CREATE); + assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, + shuffledArray(allStates))); + + mClientRecord.setState(ON_START); + assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, + shuffledArray(allStates))); + + mClientRecord.setState(ON_RESUME); + assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, + shuffledArray(allStates))); + + mClientRecord.setState(ON_PAUSE); + assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, + shuffledArray(allStates))); + + mClientRecord.setState(ON_STOP); + assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, + shuffledArray(allStates))); + + mClientRecord.setState(ON_DESTROY); + assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, + shuffledArray(allStates))); + + mClientRecord.setState(PRE_ON_CREATE); + assertEquals(PRE_ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, + new int[] {PRE_ON_CREATE})); + } + + @Test + public void testClosestStateResolution() { + mClientRecord.setState(PRE_ON_CREATE); + assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}))); + assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}))); + assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}))); + assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_PAUSE, ON_STOP, ON_DESTROY}))); + assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_STOP, ON_DESTROY}))); + assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_DESTROY}))); + } + + @Test + public void testClosestStateResolutionFromOnCreate() { + mClientRecord.setState(ON_CREATE); + assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}))); + } + + @Test + public void testClosestStateResolutionFromOnStart() { + mClientRecord.setState(ON_START); + assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}))); + assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE}))); + } + + @Test + public void testClosestStateResolutionFromOnResume() { + mClientRecord.setState(ON_RESUME); + assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START, ON_PAUSE, ON_STOP, ON_DESTROY}))); + assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_DESTROY}))); + assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START}))); + assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE}))); + } + + @Test + public void testClosestStateResolutionFromOnPause() { + mClientRecord.setState(ON_PAUSE); + assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START, ON_RESUME, ON_DESTROY}))); + assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START, ON_STOP, ON_DESTROY}))); + assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START}))); + assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE}))); + } + + @Test + public void testClosestStateResolutionFromOnStop() { + mClientRecord.setState(ON_STOP); + assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY}))); + assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_DESTROY}))); + assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}))); + } + + @Test + public void testClosestStateResolutionFromOnDestroy() { + mClientRecord.setState(ON_DESTROY); + assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP}))); + } + + @Test + public void testClosestStateResolutionToUndefined() { + mClientRecord.setState(ON_CREATE); + assertEquals(UNDEFINED, + mExecutorHelper.getClosestPreExecutionState(mClientRecord, UNDEFINED)); + } + + @Test + public void testClosestStateResolutionToOnResume() { + mClientRecord.setState(ON_DESTROY); + assertEquals(ON_START, + mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME)); + mClientRecord.setState(ON_START); + assertEquals(ON_START, + mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME)); + mClientRecord.setState(ON_PAUSE); + assertEquals(ON_PAUSE, + mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME)); + } + + private static int[] shuffledArray(int[] inputArray) { + final List list = Arrays.stream(inputArray).boxed().collect(Collectors.toList()); + Collections.shuffle(list); + return list.stream().mapToInt(Integer::intValue).toArray(); } private int[] path(int finish) { - mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish, - false /* excludeLastState */); - return mExecutor.getLifecycleSequence(); + return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish, + false /* excludeLastState */).toArray(); } private int[] pathExcludeLast(int finish) { - mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish, - true /* excludeLastState */); - return mExecutor.getLifecycleSequence(); + return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish, + true /* excludeLastState */).toArray(); } } diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java new file mode 100644 index 0000000000000000000000000000000000000000..8deccb7ffa7fe8701d73c5e24954042a01413d6d --- /dev/null +++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java @@ -0,0 +1,60 @@ +/* + * 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.content; + +import static org.junit.Assert.fail; + +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class BroadcastReceiverTests { + + private static final int RECEIVER_LIMIT_PER_APP = 1000; + private static final class EmptyReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // Empty + } + } + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + } + + @Test + public void testReceiverLimit() { + final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction"); + try { + for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) { + mContext.registerReceiver(new EmptyReceiver(), mockFilter); + } + fail("No exception thrown when registering " + + (RECEIVER_LIMIT_PER_APP + 1) + " receivers"); + } catch (IllegalStateException ise) { + // Expected + } + } +} diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java index b28a4b5ae6288aff99658ee80a9380698f427b3e..781e343e8139461a479672f99017a95c9ca95ac1 100644 --- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java +++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java @@ -1,15 +1,11 @@ package android.graphics.drawable; -import static org.junit.Assert.assertTrue; - import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Outline; -import android.graphics.Paint; import android.graphics.Path; import android.graphics.Path.Direction; import android.graphics.Rect; @@ -19,10 +15,12 @@ import android.support.test.filters.LargeTest; import android.test.AndroidTestCase; import android.util.Log; import android.util.PathParser; + +import org.junit.Test; + import java.io.File; import java.io.FileOutputStream; import java.util.Arrays; -import org.junit.Test; @LargeTest public class AdaptiveIconDrawableTest extends AndroidTestCase { @@ -173,6 +171,28 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase { assertTrue("outline path should be convex", outline.mPath.isConvex()); } + @Test + public void testSetAlpha() throws Exception { + mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable); + mIconDrawable.setBounds(0, 0, 100, 100); + + Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + + mIconDrawable.draw(canvas); + assertEquals(255, Color.alpha(bitmap.getPixel(50, 50))); + + mIconDrawable.setAlpha(200); + bitmap.eraseColor(Color.TRANSPARENT); + mIconDrawable.draw(canvas); + assertEquals(200, Color.alpha(bitmap.getPixel(50, 50))); + + mIconDrawable.setAlpha(100); + bitmap.eraseColor(Color.TRANSPARENT); + mIconDrawable.draw(canvas); + assertEquals(100, Color.alpha(bitmap.getPixel(50, 50))); + } + // // Utils // diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index f4d4c444da9b5253dfe082b1108fa0eabecafbba..22a261e354533dc5d90d2067b8722b60d27026c5 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -231,6 +231,7 @@ public class SettingsBackupTest { Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, + Settings.Global.WIFI_ON_WHEN_PROXY_DISCONNECTED, Settings.Global.FSTRIM_MANDATORY_INTERVAL, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, Settings.Global.GLOBAL_HTTP_PROXY_HOST, @@ -262,6 +263,7 @@ public class SettingsBackupTest { Settings.Global.LOW_BATTERY_SOUND, Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, Settings.Global.LOW_POWER_MODE, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, Settings.Global.LTE_SERVICE_FORCED, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MDC_INITIAL_MAX_RETRY, @@ -314,6 +316,8 @@ public class SettingsBackupTest { Settings.Global.NTP_SERVER, Settings.Global.NTP_TIMEOUT, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, + Settings.Global.OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED, + Settings.Global.OFF_BODY_RADIOS_OFF_DELAY_MS, Settings.Global.OVERLAY_DISPLAY_DEVICES, Settings.Global.PAC_CHANGE_DELAY, Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE, diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index ba9b963796f9640bac79f68a8e0903d2a967b9f6..8db1bfa2701c5ffd519b7bf6cb3f5df31ce5df4e 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -16,8 +16,13 @@ package android.view.accessibility; +import static org.hamcrest.Matchers.emptyCollectionOf; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import android.os.Parcel; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; import android.util.ArraySet; @@ -77,4 +82,36 @@ public class AccessibilityNodeInfoTest { } } + @Test + public void testStandardActions_allComeThroughParceling() { + for (AccessibilityAction action : AccessibilityAction.sStandardActions) { + final AccessibilityNodeInfo nodeWithAction = AccessibilityNodeInfo.obtain(); + nodeWithAction.addAction(action); + assertThat(nodeWithAction.getActionList(), hasItem(action)); + final Parcel parcel = Parcel.obtain(); + nodeWithAction.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + final AccessibilityNodeInfo unparceledNode = + AccessibilityNodeInfo.CREATOR.createFromParcel(parcel); + assertThat(unparceledNode.getActionList(), hasItem(action)); + } + } + + @Test + public void testEmptyListOfActions_parcelsCorrectly() { + // Also set text, as if there's nothing else in the parcel it can unparcel even with + // a bug present. + final String text = "text"; + final AccessibilityNodeInfo nodeWithEmptyActionList = AccessibilityNodeInfo.obtain(); + nodeWithEmptyActionList.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); + nodeWithEmptyActionList.removeAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); + nodeWithEmptyActionList.setText(text); + final Parcel parcel = Parcel.obtain(); + nodeWithEmptyActionList.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + final AccessibilityNodeInfo unparceledNode = + AccessibilityNodeInfo.CREATOR.createFromParcel(parcel); + assertThat(unparceledNode.getActionList(), emptyCollectionOf(AccessibilityAction.class)); + assertThat(unparceledNode.getText(), equalTo(text)); + } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..54007fb98e67104dea7099c6bbee6a50195de7b7 --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java @@ -0,0 +1,117 @@ +/* + * 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. + */ + +package android.view.textclassifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TextClassificationConstantsTest { + + @Test + public void testLoadFromString() { + final String s = "local_textclassifier_enabled=true," + + "system_textclassifier_enabled=true," + + "model_dark_launch_enabled=true," + + "smart_selection_enabled=true," + + "smart_text_share_enabled=true," + + "smart_linkify_enabled=true," + + "smart_select_animation_enabled=true," + + "suggest_selection_max_range_length=10," + + "classify_text_max_range_length=11," + + "generate_links_max_text_length=12," + + "generate_links_log_sample_rate=13"; + final TextClassificationConstants constants = + TextClassificationConstants.loadFromString(s); + assertTrue("local_textclassifier_enabled", + constants.isLocalTextClassifierEnabled()); + assertTrue("system_textclassifier_enabled", + constants.isSystemTextClassifierEnabled()); + assertTrue("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled()); + assertTrue("smart_selection_enabled", constants.isSmartSelectionEnabled()); + assertTrue("smart_text_share_enabled", constants.isSmartTextShareEnabled()); + assertTrue("smart_linkify_enabled", constants.isSmartLinkifyEnabled()); + assertTrue("smart_select_animation_enabled", constants.isSmartSelectionAnimationEnabled()); + assertEquals("suggest_selection_max_range_length", + 10, constants.getSuggestSelectionMaxRangeLength()); + assertEquals("classify_text_max_range_length", + 11, constants.getClassifyTextMaxRangeLength()); + assertEquals("generate_links_max_text_length", + 12, constants.getGenerateLinksMaxTextLength()); + assertEquals("generate_links_log_sample_rate", + 13, constants.getGenerateLinksLogSampleRate()); + } + + @Test + public void testLoadFromString_differentValues() { + final String s = "local_textclassifier_enabled=false," + + "system_textclassifier_enabled=false," + + "model_dark_launch_enabled=false," + + "smart_selection_enabled=false," + + "smart_text_share_enabled=false," + + "smart_linkify_enabled=false," + + "smart_select_animation_enabled=false," + + "suggest_selection_max_range_length=8," + + "classify_text_max_range_length=7," + + "generate_links_max_text_length=6," + + "generate_links_log_sample_rate=5"; + final TextClassificationConstants constants = + TextClassificationConstants.loadFromString(s); + assertFalse("local_textclassifier_enabled", + constants.isLocalTextClassifierEnabled()); + assertFalse("system_textclassifier_enabled", + constants.isSystemTextClassifierEnabled()); + assertFalse("model_dark_launch_enabled", constants.isModelDarkLaunchEnabled()); + assertFalse("smart_selection_enabled", constants.isSmartSelectionEnabled()); + assertFalse("smart_text_share_enabled", constants.isSmartTextShareEnabled()); + assertFalse("smart_linkify_enabled", constants.isSmartLinkifyEnabled()); + assertFalse("smart_select_animation_enabled", + constants.isSmartSelectionAnimationEnabled()); + assertEquals("suggest_selection_max_range_length", + 8, constants.getSuggestSelectionMaxRangeLength()); + assertEquals("classify_text_max_range_length", + 7, constants.getClassifyTextMaxRangeLength()); + assertEquals("generate_links_max_text_length", + 6, constants.getGenerateLinksMaxTextLength()); + assertEquals("generate_links_log_sample_rate", + 5, constants.getGenerateLinksLogSampleRate()); + } + + @Test + public void testEntityListParsing() { + final TextClassificationConstants constants = TextClassificationConstants.loadFromString( + "entity_list_default=phone," + + "entity_list_not_editable=address:flight," + + "entity_list_editable=date:datetime"); + assertEquals(1, constants.getEntityListDefault().size()); + assertEquals("phone", constants.getEntityListDefault().get(0)); + assertEquals(2, constants.getEntityListNotEditable().size()); + assertEquals("address", constants.getEntityListNotEditable().get(0)); + assertEquals("flight", constants.getEntityListNotEditable().get(1)); + assertEquals(2, constants.getEntityListEditable().size()); + assertEquals("date", constants.getEntityListEditable().get(0)); + assertEquals("datetime", constants.getEntityListEditable().get(1)); + } +} diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 5407ce69abcfcc923eef0a39a8f17fb2afe97ea1..57db153ddcd5665feb3ca8bd65586c134661e7a4 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -22,7 +22,9 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import android.content.Context; import android.os.LocaleList; +import android.service.textclassifier.TextClassifierService; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -44,6 +46,7 @@ public class TextClassificationManagerTest { private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US"); private static final String NO_TYPE = null; + private Context mContext; private TextClassificationManager mTcm; private TextClassifier mClassifier; private TextSelection.Options mSelectionOptions; @@ -52,8 +55,8 @@ public class TextClassificationManagerTest { @Before public void setup() { - mTcm = InstrumentationRegistry.getTargetContext() - .getSystemService(TextClassificationManager.class); + mContext = InstrumentationRegistry.getTargetContext(); + mTcm = mContext.getSystemService(TextClassificationManager.class); mTcm.setTextClassifier(null); mClassifier = mTcm.getTextClassifier(); mSelectionOptions = new TextSelection.Options().setDefaultLocales(LOCALES); @@ -282,6 +285,18 @@ public class TextClassificationManagerTest { assertEquals(classifier, mTcm.getTextClassifier()); } + @Test + public void testGetLocalTextClassifier() { + assertTrue(mTcm.getTextClassifier(TextClassifier.LOCAL) instanceof TextClassifierImpl); + } + + @Test + public void testGetSystemTextClassifier() { + assertTrue( + TextClassifierService.getServiceComponentName(mContext) == null + || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier); + } + private boolean isTextClassifierDisabled() { return mClassifier == TextClassifier.NO_OP; } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java deleted file mode 100644 index 984eede5568431661a4eaf1c9de867ea58a14df5..0000000000000000000000000000000000000000 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java +++ /dev/null @@ -1,46 +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. - */ - -package android.view.textclassifier; - -import static org.junit.Assert.assertEquals; - -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class TextClassifierConstantsTest { - - @Test - public void testEntityListParsing() { - final TextClassifierConstants constants = TextClassifierConstants.loadFromString( - "entity_list_default=phone," - + "entity_list_not_editable=address:flight," - + "entity_list_editable=date:datetime"); - assertEquals(1, constants.getEntityListDefault().size()); - assertEquals("phone", constants.getEntityListDefault().get(0)); - assertEquals(2, constants.getEntityListNotEditable().size()); - assertEquals("address", constants.getEntityListNotEditable().get(0)); - assertEquals("flight", constants.getEntityListNotEditable().get(1)); - assertEquals(2, constants.getEntityListEditable().size()); - assertEquals("date", constants.getEntityListEditable().get(0)); - assertEquals("datetime", constants.getEntityListEditable().get(1)); - } -} diff --git a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java index 9fa9be9359f6f7347c75becbf9b964720fa7bf55..6129b5b4c7e94f0822f1c1dd5f4cc7e423509b60 100644 --- a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java +++ b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java @@ -16,12 +16,10 @@ package android.widget.layout.linear; -import junit.framework.Assert; - -import android.widget.EditText; import android.content.Context; -import android.util.AttributeSet; import android.text.BoringLayout; +import android.util.AttributeSet; +import android.widget.EditText; /** @@ -50,10 +48,8 @@ public class ExceptionTextView extends EditText { } @Override - protected void makeNewLayout(int w, int hintWidth, - BoringLayout.Metrics boring, - BoringLayout.Metrics hintMetrics, - int ellipsizedWidth, boolean bringIntoView) { + public void makeNewLayout(int w, int hintWidth, BoringLayout.Metrics boring, + BoringLayout.Metrics hintMetrics, int ellipsizedWidth, boolean bringIntoView) { if (w < 0) { mFailed = true; w = 100; diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index a15d33711252692583a94bc34b7ee855af9d25ab..f3e10e033caf7ff116dc0b3fc68ec1242fbf271f 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -213,6 +213,20 @@ public class AccessibilityShortcutControllerTest { assertTrue(getController().isAccessibilityShortcutAvailable(true)); } + @Test + public void testShortcutAvailable_onLockScreenAndLockScreenPreferenceUnset() { + // When the user hasn't specified a lock screen preference, we allow from the lock screen + // as long as the user has agreed to enable the shortcut + configureValidShortcutService(); + configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); + Settings.Secure.putString( + mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, null); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + assertFalse(getController().isAccessibilityShortcutAvailable(true)); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); + assertTrue(getController().isAccessibilityShortcutAvailable(true)); + } + @Test public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() { configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 98b7a3fde56ab80a24ce7b82754e937031930bbd..f5fe80c0f6aeb52d8d1dfd6744862c14a6dd889d 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -44,6 +44,7 @@ import org.junit.runners.Suite; KernelUidCpuClusterTimeReaderTest.class, KernelWakelockReaderTest.class, LongSamplingCounterArrayTest.class, + PowerCalculatorTest.class, PowerProfileTest.class }) public class BatteryStatsTests { diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java index 312af16cc8a665a014d80993d9e480b70314d2f5..06f51c9c42a8f2adab04426c9bc840909238383b 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java @@ -103,6 +103,30 @@ public class KernelUidCpuActiveTimeReaderTest { verifyNoMoreInteractions(mCallback); } + @Test + public void testReadAbsolute() throws Exception { + final int cores = 8; + final int[] uids = {1, 22, 333, 4444, 5555}; + + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times)); + mReader.readAbsolute(mCallback); + for (int i = 0; i < uids.length; i++) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); + } + verifyNoMoreInteractions(mCallback); + + // Verify that a second call still returns absolute values + Mockito.reset(mCallback); + final long[][] times1 = increaseTime(times); + when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1)); + mReader.readAbsolute(mCallback); + for (int i = 0; i < uids.length; i++) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times1[i])); + } + verifyNoMoreInteractions(mCallback); + } + @Test public void testReadDelta_malformedData() throws Exception { final int cores = 8; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java index d21f541345291ab0cbc5a6d507f427d506ba973b..85dce020ddd4faec8d87d0d843a59708c01cf1a3 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java @@ -113,6 +113,34 @@ public class KernelUidCpuClusterTimeReaderTest { } + @Test + public void testReadAbsolute() throws Exception { + VerifiableCallback cb = new VerifiableCallback(); + final int cores = 6; + final int[] clusters = {2, 4}; + final int[] uids = {1, 22, 333, 4444, 5555}; + + // Verify return absolute value + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times)); + mReader.readAbsolute(cb); + for (int i = 0; i < uids.length; i++) { + cb.verify(uids[i], getTotal(clusters, times[i])); + } + cb.verifyNoMoreInteractions(); + + // Verify that a second call should return the same absolute value + cb.clear(); + Mockito.reset(mProcReader); + final long[][] times1 = increaseTime(times); + when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1)); + mReader.readAbsolute(cb); + for (int i = 0; i < uids.length; i++) { + cb.verify(uids[i], getTotal(clusters, times1[i])); + } + cb.verifyNoMoreInteractions(); + } + @Test public void testReadDelta_malformedData() throws Exception { final int cores = 6; diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java index 09507218d01a22fdb9866734a0b5eccf63e3dc20..a6b99c30315a5616c7109c4d93f24a447cfc69f3 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java @@ -296,6 +296,46 @@ public class KernelUidCpuFreqTimeReaderTest { cb.verifyNoMoreInteractions(); } + @Test + public void testReadAbsolute() throws Exception { + VerifiableCallback cb = new VerifiableCallback(); + final long[] freqs = {110, 123, 145, 167, 289, 997}; + final int[] uids = {1, 22, 333, 444, 555}; + final long[][] times = new long[uids.length][freqs.length]; + for (int i = 0; i < uids.length; ++i) { + for (int j = 0; j < freqs.length; ++j) { + times[i][j] = uids[i] * freqs[j] * 10; + } + } + when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs)); + long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile); + + assertArrayEquals(freqs, actualFreqs); + // Verify that the absolute values are returned + when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times)); + mKernelUidCpuFreqTimeReader.readAbsolute(cb); + for (int i = 0; i < uids.length; ++i) { + cb.verify(uids[i], times[i]); + } + cb.verifyNoMoreInteractions(); + + // Verify that a second call should still return absolute values + cb.clear(); + Mockito.reset(mProcReader); + final long[][] newTimes1 = new long[uids.length][freqs.length]; + for (int i = 0; i < uids.length; ++i) { + for (int j = 0; j < freqs.length; ++j) { + newTimes1[i][j] = times[i][j] + (uids[i] + freqs[j]) * 50; + } + } + when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1)); + mKernelUidCpuFreqTimeReader.readAbsolute(cb); + for (int i = 0; i < uids.length; ++i) { + cb.verify(uids[i], newTimes1[i]); + } + cb.verifyNoMoreInteractions(); + } + @Test public void testReadDelta_malformedData() throws Exception { final long[] freqs = {1, 12, 123, 1234, 12345, 123456}; diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..14d62e0b2f9c0b83ea34131f557a60d5c95225a7 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java @@ -0,0 +1,71 @@ +/* + * 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 com.android.internal.os; + + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.BatteryStats; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import junit.framework.TestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class PowerCalculatorTest extends TestCase { + private static final long US_IN_HR = 1000L * 1000L * 60L * 60L; + + @Mock + private PowerProfile mPowerProfile; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + /** Test {@link MediaPowerCalculator#calculateApp} */ + @Test + public void testMediaPowerCalculator() { + when(mPowerProfile.getAveragePower(PowerProfile.POWER_AUDIO)).thenReturn(12.0); + when(mPowerProfile.getAveragePower(PowerProfile.POWER_VIDEO)).thenReturn(25.0); + + BatteryStats.Uid u = mock(BatteryStats.Uid.class); + BatteryStats.Timer audioTimer = mock(BatteryStats.Timer.class); + when(u.getAudioTurnedOnTimer()).thenReturn(audioTimer); + when(audioTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(2L * US_IN_HR); + BatteryStats.Timer videoTimer = mock(BatteryStats.Timer.class); + when(u.getVideoTurnedOnTimer()).thenReturn(videoTimer); + when(videoTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(1L * US_IN_HR); + + MediaPowerCalculator mediaPowerCalculator = new MediaPowerCalculator(mPowerProfile); + BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, null, 0); + + mediaPowerCalculator.calculateApp(app, u, 2L * US_IN_HR, 2L * US_IN_HR, 0); + assertEquals(49.0, app.sumPower()); + } + + +} diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java index eb7da9ca4ffc27365895f9177d2cbed464e9fa8a..2853c965e87117f315d2b9da253c546fae99097d 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -53,6 +53,9 @@ public class PowerProfileTest extends TestCase { assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1)); assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3)); assertEquals(3000.0, mProfile.getBatteryCapacity()); + assertEquals(0.5, mProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY)); + assertEquals(100.0, mProfile.getAveragePower(PowerProfile.POWER_AUDIO)); + assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO)); } } diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c8994ddc457f679e620eb61f66e876582be57973 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java @@ -0,0 +1,101 @@ +/* + * 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 com.android.internal.policy; + +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import android.content.Context; +import android.platform.test.annotations.Presubmit; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.FlakyTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.ActionMode; +import android.view.ContextThemeWrapper; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests {@link PhoneWindow}'s {@link ActionMode} related methods. + */ +@SmallTest +@Presubmit +@FlakyTest(detail = "Promote to presubmit once monitored to be stable.") +@RunWith(AndroidJUnit4.class) +public final class PhoneWindowTest { + + private PhoneWindow mPhoneWindow; + private Context mContext; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getContext(); + } + + @Test + public void layoutInDisplayCutoutMode_unset() throws Exception { + createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeUnset); + installDecor(); + + assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode, + is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)); + } + + @Test + public void layoutInDisplayCutoutMode_default() throws Exception { + createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeDefault); + installDecor(); + + assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode, + is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)); + } + + @Test + public void layoutInDisplayCutoutMode_always() throws Exception { + createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeAlways); + installDecor(); + + assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode, + is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)); + } + + @Test + public void layoutInDisplayCutoutMode_never() throws Exception { + createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeNever); + installDecor(); + + assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode, + is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER)); + } + + private void createPhoneWindowWithTheme(int theme) { + mPhoneWindow = new PhoneWindow(new ContextThemeWrapper(mContext, theme)); + } + + private void installDecor() { + mPhoneWindow.getDecorView(); + } +} \ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java b/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3518527107739526721bb39523ee2d290438b775 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java @@ -0,0 +1,92 @@ +/* + * 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 com.android.internal.textservice; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.IntUnaryOperator; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LazyIntToIntMapTest { + @Test + public void testLaziness() { + final IntUnaryOperator func = mock(IntUnaryOperator.class); + when(func.applyAsInt(eq(1))).thenReturn(11); + when(func.applyAsInt(eq(2))).thenReturn(22); + + final LazyIntToIntMap map = new LazyIntToIntMap(func); + + verify(func, never()).applyAsInt(anyInt()); + + assertEquals(22, map.get(2)); + verify(func, times(0)).applyAsInt(eq(1)); + verify(func, times(1)).applyAsInt(eq(2)); + + // Accessing to the same key does not evaluate the function again. + assertEquals(22, map.get(2)); + verify(func, times(0)).applyAsInt(eq(1)); + verify(func, times(1)).applyAsInt(eq(2)); + } + + @Test + public void testDelete() { + final IntUnaryOperator func1 = mock(IntUnaryOperator.class); + when(func1.applyAsInt(eq(1))).thenReturn(11); + when(func1.applyAsInt(eq(2))).thenReturn(22); + + final IntUnaryOperator func2 = mock(IntUnaryOperator.class); + when(func2.applyAsInt(eq(1))).thenReturn(111); + when(func2.applyAsInt(eq(2))).thenReturn(222); + + final AtomicReference funcRef = new AtomicReference<>(func1); + final LazyIntToIntMap map = new LazyIntToIntMap(i -> funcRef.get().applyAsInt(i)); + + verify(func1, never()).applyAsInt(anyInt()); + verify(func2, never()).applyAsInt(anyInt()); + + assertEquals(22, map.get(2)); + verify(func1, times(1)).applyAsInt(eq(2)); + verify(func2, times(0)).applyAsInt(eq(2)); + + // Swap func1 with func2 then invalidate the key=2 + funcRef.set(func2); + map.delete(2); + + // Calling get(2) again should re-evaluate the value. + assertEquals(222, map.get(2)); + verify(func1, times(1)).applyAsInt(eq(2)); + verify(func2, times(1)).applyAsInt(eq(2)); + + // Trying to delete non-existing keys does nothing. + map.delete(1); + } +} diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk index 6330b8eb4183681c10896f4dbe7b2dd54028686c..5e518b60a9ee28c3d73abc478db24549eacbe63c 100644 --- a/core/tests/featureflagtests/Android.mk +++ b/core/tests/featureflagtests/Android.mk @@ -12,6 +12,7 @@ LOCAL_DX_FLAGS := --core-library LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib android-support-test LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk index 1d9f62442d3f592e36b380f4763ea55bb9593eb8..cba9cbd79f7be45c054a0e33a5ad98477805b58f 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk @@ -24,6 +24,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := DownloadManagerTestApp +LOCAL_PRIVATE_PLATFORM_APIS := true ifneq ($(TARGET_BUILD_VARIANT),user) # Need to run as system app to get access to Settings. This test won't work for user builds. diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk index 30c2dcaaacaabf9a542648749886ea38c2d72e3c..73ee8b8bbdfed938d984c53a46aa2008603b42d9 100644 --- a/core/tests/notificationtests/Android.mk +++ b/core/tests/notificationtests/Android.mk @@ -10,6 +10,9 @@ LOCAL_SRC_FILES := \ LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := NotificationStressTests +# Could build against SDK if it wasn't for the @RepetitiveTest annotation. +LOCAL_PRIVATE_PLATFORM_APIS := true + LOCAL_STATIC_JAVA_LIBRARIES := \ junit \ diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk index 4ca3e4ce238977bd2bd09a27f398211ceaefbc18..680ebeb6108f8b685f2389b3da84f935fada657b 100644 --- a/core/tests/overlaytests/device/Android.mk +++ b/core/tests/overlaytests/device/Android.mk @@ -18,6 +18,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayDeviceTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_STATIC_JAVA_LIBRARIES := android-support-test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_TARGET_REQUIRED_MODULES := \ diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk index 17e20eeeda851203c835b7c735718883b0f8272d..edad4b26314c3b3b6bbb9b26f3651f3f0285c1ac 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayOne +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk index c24bea9e06e9db2f720e9c83d279d9742d2cad3a..3fae8e19fc5b08221a17f2ec9d2b2f4323546631 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayTwo +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk index dc811c51e9261535e866065a2b827414963dbea4..c352c0550034f5cdfbdd74bdba06c9cdf536d6e5 100644 --- a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk +++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayDeviceTests_FrameworkOverlay +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk index 424954947d32493d364a4b242281b8d03c1745f8..3d2410dd77a509496dbb92466ebb2204f1a664a9 100644 --- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk +++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk @@ -19,6 +19,7 @@ my_package_prefix := com.android.server.om.hosttest.signature_overlay include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_BadSignatureOverlay +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad include $(BUILD_PACKAGE) @@ -26,6 +27,7 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureStaticOverlay +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_MANIFEST_FILE := static/AndroidManifest.xml LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_static @@ -34,6 +36,7 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlay +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_CERTIFICATE := platform LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1 diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk index d26425b088748bdfbc79aee45842889c696264c5..ab3faf0b158a88fffd08c288aa252f619a8d74e7 100644 --- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk +++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk @@ -18,6 +18,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_STATIC_JAVA_LIBRARIES := android-support-test include $(BUILD_PACKAGE) @@ -27,6 +28,7 @@ my_package_prefix := com.android.server.om.hosttest.framework_overlay include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1 +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_CERTIFICATE := platform LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1 @@ -38,6 +40,7 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2 +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_CERTIFICATE := platform LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2 @@ -51,6 +54,7 @@ my_package_prefix := com.android.server.om.hosttest.app_overlay include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1 +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_CERTIFICATE := platform LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1 @@ -62,6 +66,7 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2 +LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := general-tests LOCAL_CERTIFICATE := platform LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2 diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk index 5bfde78c424a14085cf8c7363f7fc29b24a98852..f95231f1d397b65fe2fb3dcb5d01873c1c220b2e 100644 --- a/core/tests/packagemanagertests/Android.mk +++ b/core/tests/packagemanagertests/Android.mk @@ -15,6 +15,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk index 7bba4179afa0ba471eb02960eb3bd0a2e60712bc..374d0d0687c34f55f090f80e46b349ca680f4a8d 100644 --- a/core/tests/privacytests/Android.mk +++ b/core/tests/privacytests/Android.mk @@ -12,6 +12,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk index 57e205994c4ec0132c54156bdcb437ac5f9d0839..3458be01bd1142b002db3741b2c4c7010f6c6292 100644 --- a/core/tests/systemproperties/Android.mk +++ b/core/tests/systemproperties/Android.mk @@ -12,6 +12,7 @@ LOCAL_DX_FLAGS := --core-library LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk index 2dc105932f041f3922a4896bb3ea51488918d3ba..5c60c8184753522d90607aada24991f11741c1f7 100644 --- a/core/tests/utiltests/Android.mk +++ b/core/tests/utiltests/Android.mk @@ -22,6 +22,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock LOCAL_PACKAGE_NAME := FrameworksUtilTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform diff --git a/core/tests/webkit/Android.mk b/core/tests/webkit/Android.mk index cd0c3d25a68b6de62bc4321dd765faaaaa2d3ff1..45f6957c10e779f7c31c6c681458a3fc8c8d51d3 100644 --- a/core/tests/webkit/Android.mk +++ b/core/tests/webkit/Android.mk @@ -31,6 +31,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test LOCAL_PACKAGE_NAME := WebViewLoadingTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml index 1d46d42a97504bd86ce74555bdcbd0cf846e3f24..fd1027c218d84998a6367057b5491147c39a39aa 100644 --- a/data/etc/hiddenapi-package-whitelist.xml +++ b/data/etc/hiddenapi-package-whitelist.xml @@ -67,6 +67,7 @@ which apps should be allowed to access the entire private API. + diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8e76dd38aeb9c2dbe74fec80d02caade54b16993..b455419b23e39d226dc9556c609e556b823df617 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -322,6 +322,8 @@ applications that come with the platform + + @@ -347,7 +349,6 @@ applications that come with the platform - diff --git a/graphics/java/android/graphics/EmbossMaskFilter.java b/graphics/java/android/graphics/EmbossMaskFilter.java index a9e180fdb6c2b95bf3745c64591d6d736bae5366..003678ae5a3cb8fa361dc589555d9ecc3a166bd2 100644 --- a/graphics/java/android/graphics/EmbossMaskFilter.java +++ b/graphics/java/android/graphics/EmbossMaskFilter.java @@ -20,12 +20,15 @@ public class EmbossMaskFilter extends MaskFilter { /** * Create an emboss maskfilter * + * @deprecated This subclass is not supported and should not be instantiated. + * * @param direction array of 3 scalars [x, y, z] specifying the direction of the light source * @param ambient 0...1 amount of ambient light * @param specular coefficient for specular highlights (e.g. 8) * @param blurRadius amount to blur before applying lighting (e.g. 3) * @return the emboss maskfilter */ + @Deprecated public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) { if (direction.length < 3) { throw new ArrayIndexOutOfBoundsException(); diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index e7cfcfdf776032cc80cad23d3a3164f916c8b499..c69eb32148c07d34d3ebfa14ed7a251001384d60 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -24,6 +24,8 @@ import android.util.Log; import dalvik.annotation.optimization.CriticalNative; +import libcore.util.NativeAllocationRegistry; + import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -38,6 +40,14 @@ public class FontFamily { private static String TAG = "FontFamily"; + private static final NativeAllocationRegistry sBuilderRegistry = new NativeAllocationRegistry( + FontFamily.class.getClassLoader(), nGetBuilderReleaseFunc(), 64); + + private @Nullable Runnable mNativeBuilderCleaner; + + private static final NativeAllocationRegistry sFamilyRegistry = new NativeAllocationRegistry( + FontFamily.class.getClassLoader(), nGetFamilyReleaseFunc(), 64); + /** * @hide */ @@ -48,6 +58,7 @@ public class FontFamily { public FontFamily() { mBuilderPtr = nInitBuilder(null, 0); + mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr); } public FontFamily(@Nullable String[] langs, int variant) { @@ -60,6 +71,7 @@ public class FontFamily { langsString = TextUtils.join(",", langs); } mBuilderPtr = nInitBuilder(langsString, variant); + mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr); } /** @@ -73,7 +85,11 @@ public class FontFamily { throw new IllegalStateException("This FontFamily is already frozen"); } mNativePtr = nCreateFamily(mBuilderPtr); + mNativeBuilderCleaner.run(); mBuilderPtr = 0; + if (mNativePtr != 0) { + sFamilyRegistry.registerNativeAllocation(this, mNativePtr); + } return mNativePtr != 0; } @@ -81,24 +97,10 @@ public class FontFamily { if (mBuilderPtr == 0) { throw new IllegalStateException("This FontFamily is already frozen or abandoned"); } - nAbort(mBuilderPtr); + mNativeBuilderCleaner.run(); mBuilderPtr = 0; } - @Override - protected void finalize() throws Throwable { - try { - if (mNativePtr != 0) { - nUnrefFamily(mNativePtr); - } - if (mBuilderPtr != 0) { - nAbort(mBuilderPtr); - } - } finally { - super.finalize(); - } - } - public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight, int italic) { if (mBuilderPtr == 0) { @@ -171,10 +173,10 @@ public class FontFamily { private static native long nCreateFamily(long mBuilderPtr); @CriticalNative - private static native void nAbort(long mBuilderPtr); + private static native long nGetBuilderReleaseFunc(); @CriticalNative - private static native void nUnrefFamily(long nativePtr); + private static native long nGetFamilyReleaseFunc(); // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font. // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font. private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex, diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 3cca47b47a5951f1854852d1a244e278c26d3525..88701fac66c761871e961354a8cb111482d5c8c0 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -19,6 +19,8 @@ package android.graphics; import static android.system.OsConstants.SEEK_CUR; import static android.system.OsConstants.SEEK_SET; +import static java.lang.annotation.RetentionPolicy.SOURCE; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -28,32 +30,28 @@ import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager.AssetInputStream; import android.content.res.Resources; import android.graphics.drawable.AnimatedImageDrawable; -import android.graphics.drawable.Drawable; import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; import android.net.Uri; -import android.util.Size; import android.system.ErrnoException; import android.system.Os; import android.util.DisplayMetrics; +import android.util.Size; import android.util.TypedValue; -import libcore.io.IoUtils; import dalvik.system.CloseGuard; -import java.nio.ByteBuffer; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.lang.ArrayIndexOutOfBoundsException; -import java.lang.AutoCloseable; -import java.lang.NullPointerException; -import java.lang.RuntimeException; import java.lang.annotation.Retention; -import static java.lang.annotation.RetentionPolicy.SOURCE; +import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -494,8 +492,8 @@ public final class ImageDecoder implements AutoCloseable { private int mAllocator = ALLOCATOR_DEFAULT; private boolean mRequireUnpremultiplied = false; private boolean mMutable = false; - private boolean mPreferRamOverQuality = false; - private boolean mAsAlphaMask = false; + private boolean mConserveMemory = false; + private boolean mDecodeAsAlphaMask = false; private Rect mCropRect; private Rect mOutPaddingRect; private Source mSource; @@ -623,16 +621,18 @@ public final class ImageDecoder implements AutoCloseable { /** * Create a new {@link Source} from a {@link java.nio.ByteBuffer}. * - *

    The returned {@link Source} effectively takes ownership of the - * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after - * this call.

    + *

    Decoding will start from {@link java.nio.ByteBuffer#position()}. The + * position of {@code buffer} will not be affected.

    * - * Decoding will start from {@link java.nio.ByteBuffer#position()}. The - * position after decoding is undefined. + *

    Note: If this {@code Source} is passed to {@link #decodeDrawable}, and + * the encoded image is animated, the returned {@link AnimatedImageDrawable} + * will continue reading from the {@code buffer}, so its contents must not + * be modified, even after the {@code AnimatedImageDrawable} is returned. + * {@code buffer}'s contents should never be modified during decode.

    */ @NonNull public static Source createSource(@NonNull ByteBuffer buffer) { - return new ByteBufferSource(buffer); + return new ByteBufferSource(buffer.slice()); } /** @@ -692,8 +692,9 @@ public final class ImageDecoder implements AutoCloseable { * * @param width must be greater than 0. * @param height must be greater than 0. + * @return this object for chaining. */ - public void setResize(int width, int height) { + public ImageDecoder setResize(int width, int height) { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("Dimensions must be positive! " + "provided (" + width + ", " + height + ")"); @@ -701,6 +702,7 @@ public final class ImageDecoder implements AutoCloseable { mDesiredWidth = width; mDesiredHeight = height; + return this; } /** @@ -710,10 +712,11 @@ public final class ImageDecoder implements AutoCloseable { * {@link #getSampledSize} to {@link #setResize(int, int)}.

    * * @param sampleSize Sampling rate of the encoded image. + * @return this object for chaining. */ - public void setResize(int sampleSize) { + public ImageDecoder setResize(int sampleSize) { Size size = this.getSampledSize(sampleSize); - this.setResize(size.getWidth(), size.getHeight()); + return this.setResize(size.getWidth(), size.getHeight()); } private boolean requestedResize() { @@ -727,7 +730,7 @@ public final class ImageDecoder implements AutoCloseable { * Will typically result in a {@link Bitmap.Config#HARDWARE} * allocation, but may be software for small images. In addition, this will * switch to software when HARDWARE is incompatible, e.g. - * {@link #setMutable}, {@link #setAsAlphaMask}. + * {@link #setMutable}, {@link #setDecodeAsAlphaMask}. */ public static final int ALLOCATOR_DEFAULT = 0; @@ -750,7 +753,7 @@ public final class ImageDecoder implements AutoCloseable { * Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}. * * When this is combined with incompatible options, like - * {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable} + * {@link #setMutable} or {@link #setDecodeAsAlphaMask}, {@link #decodeDrawable} * / {@link #decodeBitmap} will throw an * {@link java.lang.IllegalStateException}. */ @@ -758,8 +761,9 @@ public final class ImageDecoder implements AutoCloseable { /** @hide **/ @Retention(SOURCE) - @IntDef({ ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, ALLOCATOR_SHARED_MEMORY, - ALLOCATOR_HARDWARE }) + @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, + ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE }, + prefix = {"ALLOCATOR_"}) public @interface Allocator {}; /** @@ -768,18 +772,28 @@ public final class ImageDecoder implements AutoCloseable { * This is ignored for animated drawables. * * @param allocator Type of allocator to use. + * @return this object for chaining. */ - public void setAllocator(@Allocator int allocator) { + public ImageDecoder setAllocator(@Allocator int allocator) { if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) { throw new IllegalArgumentException("invalid allocator " + allocator); } mAllocator = allocator; + return this; + } + + /** + * Return the allocator for the pixel memory. + */ + @Allocator + public int getAllocator() { + return mAllocator; } /** * Specify whether the {@link Bitmap} should have unpremultiplied pixels. * - * By default, ImageDecoder will create a {@link Bitmap} with + *

    By default, ImageDecoder will create a {@link Bitmap} with * premultiplied pixels, which is required for drawing with the * {@link android.view.View} system (i.e. to a {@link Canvas}). Calling * this method with a value of {@code true} will result in @@ -787,9 +801,20 @@ public final class ImageDecoder implements AutoCloseable { * pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with * {@link #decodeDrawable}; attempting to decode an unpremultiplied * {@link Drawable} will throw an {@link java.lang.IllegalStateException}. + *

    + * + * @return this object for chaining. */ - public void setRequireUnpremultiplied(boolean requireUnpremultiplied) { + public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) { mRequireUnpremultiplied = requireUnpremultiplied; + return this; + } + + /** + * Return whether the {@link Bitmap} will have unpremultiplied pixels. + */ + public boolean getRequireUnpremultiplied() { + return mRequireUnpremultiplied; } /** @@ -804,19 +829,41 @@ public final class ImageDecoder implements AutoCloseable { *

    For an animated image, the drawing commands drawn on the * {@link Canvas} will be recorded immediately and then applied to each * frame.

    + * + * @return this object for chaining. */ - public void setPostProcessor(@Nullable PostProcessor p) { + public ImageDecoder setPostProcessor(@Nullable PostProcessor p) { mPostProcessor = p; + return this; + } + + /** + * Return the {@link PostProcessor} currently set. + */ + @Nullable + public PostProcessor getPostProcessor() { + return mPostProcessor; } /** * Set (replace) the {@link OnPartialImageListener} on this object. * - * Will be called if there is an error in the input. Without one, a - * partial {@link Bitmap} will be created. + *

    Will be called if there is an error in the input. Without one, an + * error will result in an Exception being thrown.

    + * + * @return this object for chaining. */ - public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { + public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) { mOnPartialImageListener = l; + return this; + } + + /** + * Return the {@link OnPartialImageListener} currently set. + */ + @Nullable + public OnPartialImageListener getOnPartialImageListener() { + return mOnPartialImageListener; } /** @@ -830,9 +877,20 @@ public final class ImageDecoder implements AutoCloseable { *

    NOT intended as a replacement for * {@link BitmapRegionDecoder#decodeRegion}. This supports all formats, * but merely crops the output.

    + * + * @return this object for chaining. */ - public void setCrop(@Nullable Rect subset) { + public ImageDecoder setCrop(@Nullable Rect subset) { mCropRect = subset; + return this; + } + + /** + * Return the cropping rectangle, if set. + */ + @Nullable + public Rect getCrop() { + return mCropRect; } /** @@ -841,10 +899,13 @@ public final class ImageDecoder implements AutoCloseable { * If the image is a nine patch, this Rect will be set to the padding * rectangle during decode. Otherwise it will not be modified. * + * @return this object for chaining. + * * @hide */ - public void setOutPaddingRect(@NonNull Rect outPadding) { + public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) { mOutPaddingRect = outPadding; + return this; } /** @@ -862,21 +923,49 @@ public final class ImageDecoder implements AutoCloseable { * which would require retrieving the Bitmap from the returned Drawable in * order to modify. Attempting to decode a mutable {@link Drawable} will * throw an {@link java.lang.IllegalStateException}.

    + * + * @return this object for chaining. */ - public void setMutable(boolean mutable) { + public ImageDecoder setMutable(boolean mutable) { mMutable = mutable; + return this; + } + + /** + * Return whether the {@link Bitmap} will be mutable. + */ + public boolean getMutable() { + return mMutable; } /** * Specify whether to potentially save RAM at the expense of quality. * - * Setting this to {@code true} may result in a {@link Bitmap} with a - * denser {@link Bitmap.Config}, depending on the image. For example, for - * an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config} - * with no alpha information. + *

    Setting this to {@code true} may result in a {@link Bitmap} with a + * denser {@link Bitmap.Config}, depending on the image. For example, an + * opaque {@link Bitmap} with 8 bits or precision for each of its red, + * green and blue components would decode to + * {@link Bitmap.Config#ARGB_8888} by default, but setting this to + * {@code true} will result in decoding to {@link Bitmap.Config#RGB_565}. + * This necessarily lowers the quality of the output, but saves half + * the memory used.

    + * + * @return this object for chaining. */ - public void setPreferRamOverQuality(boolean preferRamOverQuality) { - mPreferRamOverQuality = preferRamOverQuality; + public ImageDecoder setConserveMemory(boolean conserveMemory) { + mConserveMemory = conserveMemory; + return this; + } + + /** + * Return whether this object will try to save RAM at the expense of quality. + * + *

    This returns whether {@link #setConserveMemory} was set to {@code true}. + * It may still return {@code true} even if the {@code ImageDecoder} does not + * have a way to save RAM at the expense of quality for this image.

    + */ + public boolean getConserveMemory() { + return mConserveMemory; } /** @@ -886,13 +975,43 @@ public final class ImageDecoder implements AutoCloseable { * with only one channel, treat that channel as alpha. Otherwise this call has * no effect.

    * - *

    setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to + *

    setDecodeAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to * combine them will result in {@link #decodeDrawable}/ * {@link #decodeBitmap} throwing an * {@link java.lang.IllegalStateException}.

    + * + * @return this object for chaining. + */ + public ImageDecoder setDecodeAsAlphaMask(boolean decodeAsAlphaMask) { + mDecodeAsAlphaMask = decodeAsAlphaMask; + return this; + } + + /** @removed + * @deprecated Call {@link #setDecodeAsAlphaMask} instead. + */ + @java.lang.Deprecated + public ImageDecoder setAsAlphaMask(boolean asAlphaMask) { + return this.setDecodeAsAlphaMask(asAlphaMask); + } + + /** + * Return whether to treat single channel input as alpha. + * + *

    This returns whether {@link #setDecodeAsAlphaMask} was set to {@code true}. + * It may still return {@code true} even if the image has more than one + * channel and therefore will not be treated as an alpha mask.

    + */ + public boolean getDecodeAsAlphaMask() { + return mDecodeAsAlphaMask; + } + + /** @removed + * @deprecated Call {@link #getDecodeAsAlphaMask} instead. */ - public void setAsAlphaMask(boolean asAlphaMask) { - mAsAlphaMask = asAlphaMask; + @java.lang.Deprecated + public boolean getAsAlphaMask() { + return this.getDecodeAsAlphaMask(); } @Override @@ -925,7 +1044,7 @@ public final class ImageDecoder implements AutoCloseable { if (mMutable) { throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!"); } - if (mAsAlphaMask) { + if (mDecodeAsAlphaMask) { throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!"); } } @@ -946,7 +1065,7 @@ public final class ImageDecoder implements AutoCloseable { } @NonNull - private Bitmap decodeBitmap() throws IOException { + private Bitmap decodeBitmapInternal() throws IOException { checkState(); // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener // exists @@ -957,7 +1076,7 @@ public final class ImageDecoder implements AutoCloseable { return nDecodeBitmap(mNativePtr, partialImagePtr, postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, - mPreferRamOverQuality, mAsAlphaMask); + mConserveMemory, mDecodeAsAlphaMask); } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -977,15 +1096,24 @@ public final class ImageDecoder implements AutoCloseable { * * @param src representing the encoded image. * @param listener for learning the {@link ImageInfo} and changing any - * default settings on the {@code ImageDecoder}. If not {@code null}, - * this will be called on the same thread as {@code decodeDrawable} - * before that method returns. + * default settings on the {@code ImageDecoder}. This will be called on + * the same thread as {@code decodeDrawable} before that method returns. * @return Drawable for displaying the image. * @throws IOException if {@code src} is not found, is an unsupported * format, or cannot be decoded for any reason. */ @NonNull public static Drawable decodeDrawable(@NonNull Source src, + @NonNull OnHeaderDecodedListener listener) throws IOException { + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null! " + + "Use decodeDrawable(Source) to not have a listener"); + } + return decodeDrawableImpl(src, listener); + } + + @NonNull + private static Drawable decodeDrawableImpl(@NonNull Source src, @Nullable OnHeaderDecodedListener listener) throws IOException { try (ImageDecoder decoder = src.createImageDecoder()) { decoder.mSource = src; @@ -1022,7 +1150,7 @@ public final class ImageDecoder implements AutoCloseable { return d; } - Bitmap bm = decoder.decodeBitmap(); + Bitmap bm = decoder.decodeBitmapInternal(); bm.setDensity(srcDensity); Resources res = src.getResources(); @@ -1049,7 +1177,7 @@ public final class ImageDecoder implements AutoCloseable { @NonNull public static Drawable decodeDrawable(@NonNull Source src) throws IOException { - return decodeDrawable(src, null); + return decodeDrawableImpl(src, null); } /** @@ -1057,15 +1185,24 @@ public final class ImageDecoder implements AutoCloseable { * * @param src representing the encoded image. * @param listener for learning the {@link ImageInfo} and changing any - * default settings on the {@code ImageDecoder}. If not {@code null}, - * this will be called on the same thread as {@code decodeBitmap} - * before that method returns. + * default settings on the {@code ImageDecoder}. This will be called on + * the same thread as {@code decodeBitmap} before that method returns. * @return Bitmap containing the image. * @throws IOException if {@code src} is not found, is an unsupported * format, or cannot be decoded for any reason. */ @NonNull public static Bitmap decodeBitmap(@NonNull Source src, + @NonNull OnHeaderDecodedListener listener) throws IOException { + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null! " + + "Use decodeBitmap(Source) to not have a listener"); + } + return decodeBitmapImpl(src, listener); + } + + @NonNull + private static Bitmap decodeBitmapImpl(@NonNull Source src, @Nullable OnHeaderDecodedListener listener) throws IOException { try (ImageDecoder decoder = src.createImageDecoder()) { decoder.mSource = src; @@ -1074,7 +1211,7 @@ public final class ImageDecoder implements AutoCloseable { // this call potentially manipulates the decoder so it must be performed prior to // decoding the bitmap final int srcDensity = computeDensity(src, decoder); - Bitmap bm = decoder.decodeBitmap(); + Bitmap bm = decoder.decodeBitmapInternal(); bm.setDensity(srcDensity); Rect padding = decoder.mOutPaddingRect; @@ -1132,7 +1269,7 @@ public final class ImageDecoder implements AutoCloseable { */ @NonNull public static Bitmap decodeBitmap(@NonNull Source src) throws IOException { - return decodeBitmap(src, null); + return decodeBitmapImpl(src, null); } /** @@ -1171,7 +1308,7 @@ public final class ImageDecoder implements AutoCloseable { int width, int height, @Nullable Rect cropRect, boolean mutable, int allocator, boolean requireUnpremul, - boolean preferRamOverQuality, boolean asAlphaMask) + boolean conserveMemory, boolean decodeAsAlphaMask) throws IOException; private static native Size nGetSampledSize(long nativePtr, int sampleSize); diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 8595165aab278ced9a3fbac3cd42613f4055636e..f27c11dbf735655ddb8daa9dee1a6c76d065222d 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -21,6 +21,7 @@ import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEnt import static android.content.res.FontResourcesParser.FontFileResourceEntry; import static android.content.res.FontResourcesParser.ProviderResourceEntry; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -41,6 +42,10 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import dalvik.annotation.optimization.CriticalNative; + +import libcore.util.NativeAllocationRegistry; + import org.xmlpull.v1.XmlPullParserException; import java.io.File; @@ -49,6 +54,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; @@ -68,6 +75,9 @@ public class Typeface { private static String TAG = "Typeface"; + private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + Typeface.class.getClassLoader(), nativeGetReleaseFunc(), 64); + /** The default NORMAL typeface object */ public static final Typeface DEFAULT; /** @@ -117,6 +127,11 @@ public class Typeface { */ public long native_instance; + /** @hide */ + @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC}) + @Retention(RetentionPolicy.SOURCE) + public @interface Style {} + // Style public static final int NORMAL = 0; public static final int BOLD = 1; @@ -124,8 +139,15 @@ public class Typeface { public static final int BOLD_ITALIC = 3; /** @hide */ public static final int STYLE_MASK = 0x03; - private int mStyle = 0; - private int mWeight = 0; + private @Style int mStyle = 0; + + /** + * A maximum value for the weight value. + * @hide + */ + public static final int MAX_WEIGHT = 1000; + + private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0; // Value for weight and italic. Indicates the value is resolved by font metadata. // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp @@ -153,7 +175,7 @@ public class Typeface { } /** Returns the typeface's intrinsic style attributes */ - public int getStyle() { + public @Style int getStyle() { return mStyle; } @@ -659,7 +681,7 @@ public class Typeface { * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC * @return The best matching typeface. */ - public static Typeface create(String familyName, int style) { + public static Typeface create(String familyName, @Style int style) { return create(sSystemFontMap.get(familyName), style); } @@ -680,7 +702,7 @@ public class Typeface { * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC * @return The best matching typeface. */ - public static Typeface create(Typeface family, int style) { + public static Typeface create(Typeface family, @Style int style) { if ((style & ~STYLE_MASK) != 0) { style = NORMAL; } @@ -776,7 +798,7 @@ public class Typeface { * * @return the default typeface that corresponds to the style */ - public static Typeface defaultFromStyle(int style) { + public static Typeface defaultFromStyle(@Style int style) { return sDefaults[style]; } @@ -896,6 +918,7 @@ public class Typeface { } native_instance = ni; + sRegistry.registerNativeAllocation(this, native_instance); mStyle = nativeGetStyle(ni); mWeight = nativeGetWeight(ni); } @@ -1104,16 +1127,6 @@ public class Typeface { } - @Override - protected void finalize() throws Throwable { - try { - nativeUnref(native_instance); - native_instance = 0; // Other finalizers can still call us. - } finally { - super.finalize(); - } - } - @Override public boolean equals(Object o) { if (this == o) return true; @@ -1158,10 +1171,18 @@ public class Typeface { private static native long nativeCreateFromTypefaceWithVariation( long native_instance, List axes); private static native long nativeCreateWeightAlias(long native_instance, int weight); - private static native void nativeUnref(long native_instance); - private static native int nativeGetStyle(long native_instance); - private static native int nativeGetWeight(long native_instance); private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic); - private static native void nativeSetDefault(long native_instance); private static native int[] nativeGetSupportedAxes(long native_instance); + + @CriticalNative + private static native void nativeSetDefault(long nativePtr); + + @CriticalNative + private static native int nativeGetStyle(long nativePtr); + + @CriticalNative + private static native int nativeGetWeight(long nativePtr); + + @CriticalNative + private static native long nativeGetReleaseFunc(); } diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java index 1d0cfa5ff08240a23e6adc72a1bba6c42d88127b..fdd638adba81b77c0aaec40455424340a91ffdf5 100644 --- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java @@ -44,6 +44,7 @@ import android.util.DisplayMetrics; import android.util.PathParser; import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -668,13 +669,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @Override public void setAlpha(int alpha) { - final ChildDrawable[] array = mLayerState.mChildren; - for (int i = 0; i < mLayerState.N_CHILDREN; i++) { - final Drawable dr = array[i].mDrawable; - if (dr != null) { - dr.setAlpha(alpha); - } - } + mPaint.setAlpha(alpha); } @Override diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index f70d6e1223dfd754f04244d71bc2ab9087b0a265..c0f49208e27e187775d6a2b28635fd4b9d73d4ea 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -34,6 +34,7 @@ import android.os.Looper; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.View; import com.android.internal.R; @@ -77,6 +78,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { int[] mThemeAttrs = null; boolean mAutoMirrored = false; + int mRepeatCount = REPEAT_UNDEFINED; } private State mState; @@ -86,33 +88,87 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { private ColorFilter mColorFilter; /** - * Pass this to {@link #setLoopCount} to loop infinitely. + * Pass this to {@link #setRepeatCount} to repeat infinitely. * *

    {@link Animatable2.AnimationCallback#onAnimationEnd} will never be * called unless there is an error.

    */ - public static final int LOOP_INFINITE = -1; + public static final int REPEAT_INFINITE = -1; + + /** @removed + * @deprecated Replaced with REPEAT_INFINITE to match other APIs. + */ + @java.lang.Deprecated + public static final int LOOP_INFINITE = REPEAT_INFINITE; + + private static final int REPEAT_UNDEFINED = -2; /** - * Specify the number of times to loop the animation. + * Specify the number of times to repeat the animation. + * + *

    By default, the repeat count in the encoded data is respected. If set + * to {@link #REPEAT_INFINITE}, the animation will repeat as long as it is + * displayed. If the value is {@code 0}, the animation will play once.

    * - *

    By default, the loop count in the encoded data is respected.

    + *

    This call replaces the current repeat count. If the encoded data + * specified a repeat count of {@code 2} (meaning that + * {@link #getRepeatCount()} returns {@code 2}, the animation will play + * three times. Calling {@code setRepeatCount(1)} will result in playing only + * twice and {@link #getRepeatCount()} returning {@code 1}.

    + * + *

    If the animation is already playing, the iterations that have already + * occurred count towards the new count. If the animation has already + * repeated the appropriate number of times (or more), it will finish its + * current iteration and then stop.

    */ - public void setLoopCount(int loopCount) { - if (mState.mNativePtr == 0) { - throw new IllegalStateException("called setLoopCount on empty AnimatedImageDrawable"); + public void setRepeatCount(@IntRange(from = REPEAT_INFINITE) int repeatCount) { + if (repeatCount < REPEAT_INFINITE) { + throw new IllegalArgumentException("invalid value passed to setRepeatCount" + + repeatCount); + } + if (mState.mRepeatCount != repeatCount) { + mState.mRepeatCount = repeatCount; + if (mState.mNativePtr != 0) { + nSetRepeatCount(mState.mNativePtr, repeatCount); + } } - nSetLoopCount(mState.mNativePtr, loopCount); + } + + /** @removed + * @deprecated Replaced with setRepeatCount to match other APIs. + */ + @java.lang.Deprecated + public void setLoopCount(int loopCount) { + setRepeatCount(loopCount); } /** - * Retrieve the number of times the animation will loop. + * Retrieve the number of times the animation will repeat. + * + *

    By default, the repeat count in the encoded data is respected. If the + * value is {@link #REPEAT_INFINITE}, the animation will repeat as long as + * it is displayed. If the value is {@code 0}, it will play once.

    + * + *

    Calling {@link #setRepeatCount} will make future calls to this method + * return the value passed to {@link #setRepeatCount}.

    */ - public int getLoopCount() { + public int getRepeatCount() { if (mState.mNativePtr == 0) { - throw new IllegalStateException("called getLoopCount on empty AnimatedImageDrawable"); + throw new IllegalStateException("called getRepeatCount on empty AnimatedImageDrawable"); + } + if (mState.mRepeatCount == REPEAT_UNDEFINED) { + mState.mRepeatCount = nGetRepeatCount(mState.mNativePtr); + } - return nGetLoopCount(mState.mNativePtr); + return mState.mRepeatCount; + } + + /** @removed + * @deprecated Replaced with getRepeatCount to match other APIs. + */ + @java.lang.Deprecated + public int getLoopCount(int loopCount) { + return getRepeatCount(); } /** @@ -176,12 +232,18 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { ": did not decode animated"); } + // This may have previously been set without a src if we were waiting for a + // theme. + final int repeatCount = mState.mRepeatCount; // Transfer the state of other to this one. other will be discarded. AnimatedImageDrawable other = (AnimatedImageDrawable) drawable; mState = other.mState; other.mState = null; mIntrinsicWidth = other.mIntrinsicWidth; mIntrinsicHeight = other.mIntrinsicHeight; + if (repeatCount != REPEAT_UNDEFINED) { + this.setRepeatCount(repeatCount); + } } mState.mThemeAttrs = a.extractThemeAttrs(); @@ -193,6 +255,12 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { mState.mAutoMirrored = a.getBoolean( R.styleable.AnimatedImageDrawable_autoMirrored, oldState.mAutoMirrored); + + int repeatCount = a.getInt( + R.styleable.AnimatedImageDrawable_repeatCount, REPEAT_UNDEFINED); + if (repeatCount != REPEAT_UNDEFINED) { + this.setRepeatCount(repeatCount); + } } /** @@ -271,7 +339,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { } @Override - public void setAlpha(@IntRange(from=0,to=255) int alpha) { + public void setAlpha(@IntRange(from = 0, to = 255) int alpha) { if (alpha < 0 || alpha > 255) { throw new IllegalArgumentException("Alpha must be between 0 and" + " 255! provided " + alpha); @@ -322,10 +390,24 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { public void setAutoMirrored(boolean mirrored) { if (mState.mAutoMirrored != mirrored) { mState.mAutoMirrored = mirrored; - invalidateSelf(); + if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL && mState.mNativePtr != 0) { + nSetMirrored(mState.mNativePtr, mirrored); + invalidateSelf(); + } } } + @Override + public boolean onLayoutDirectionChanged(int layoutDirection) { + if (!mState.mAutoMirrored || mState.mNativePtr == 0) { + return false; + } + + final boolean mirror = layoutDirection == View.LAYOUT_DIRECTION_RTL; + nSetMirrored(mState.mNativePtr, mirror); + return true; + } + @Override public final boolean isAutoMirrored() { return mState.mAutoMirrored; @@ -508,9 +590,9 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { @FastNative private static native boolean nStop(long nativePtr); @FastNative - private static native int nGetLoopCount(long nativePtr); + private static native int nGetRepeatCount(long nativePtr); @FastNative - private static native void nSetLoopCount(long nativePtr, int loopCount); + private static native void nSetRepeatCount(long nativePtr, int repeatCount); // Pass the drawable down to native so it can call onAnimationEnd. private static native void nSetOnAnimationEndListener(long nativePtr, @Nullable AnimatedImageDrawable drawable); @@ -518,4 +600,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { private static native long nNativeByteSize(long nativePtr); @FastNative private static native void nMarkInvisible(long nativePtr); + @FastNative + private static native void nSetMirrored(long nativePtr, boolean mirror); } diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index e74dc6dc96711feec88cfdeceb3ed7a7ada8c244..54358e35544437a1c1440d6b7dbcf3e21a783315 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -201,10 +201,10 @@ import dalvik.annotation.optimization.FastNative; * android:drawable="@drawable/vectordrawable" > * <target * android:name="rotationGroup" - * android:animation="@anim/rotation" /> + * android:animation="@animator/rotation" /> * <target * android:name="v" - * android:animation="@anim/path_morph" /> + * android:animation="@animator/path_morph" /> * </animated-vector> *
    * diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java index 749b75941ef95626c545bc7607e07d7b4e9f1280..361fe0bffbbc5a7b3b3ca089f1a9acef8f86e9a5 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -18,11 +18,14 @@ package android.graphics.drawable; import android.annotation.ColorInt; import android.annotation.DrawableRes; -import android.content.res.ColorStateList; +import android.annotation.IdRes; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -60,17 +63,40 @@ import java.util.Objects; public final class Icon implements Parcelable { private static final String TAG = "Icon"; - /** @hide */ + /** + * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}. + * @see #getType + */ public static final int TYPE_BITMAP = 1; - /** @hide */ + /** + * An icon that was created using {@link Icon#createWithResource}. + * @see #getType + */ public static final int TYPE_RESOURCE = 2; - /** @hide */ + /** + * An icon that was created using {@link Icon#createWithData(byte[], int, int)}. + * @see #getType + */ public static final int TYPE_DATA = 3; - /** @hide */ + /** + * An icon that was created using {@link Icon#createWithContentUri} + * or {@link Icon#createWithFilePath(String)}. + * @see #getType + */ public static final int TYPE_URI = 4; - /** @hide */ + /** + * An icon that was created using {@link Icon#createWithAdaptiveBitmap}. + * @see #getType + */ public static final int TYPE_ADAPTIVE_BITMAP = 5; + /** + * @hide + */ + @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP}) + public @interface IconType { + } + private static final int VERSION_STREAM_SERIALIZER = 1; private final int mType; @@ -99,14 +125,12 @@ public final class Icon implements Parcelable { private int mInt2; /** - * @return The type of image data held in this Icon. One of - * {@link #TYPE_BITMAP}, - * {@link #TYPE_RESOURCE}, - * {@link #TYPE_DATA}, or - * {@link #TYPE_URI}. - * {@link #TYPE_ADAPTIVE_BITMAP} - * @hide + * Gets the type of the icon provided. + *

    + * Note that new types may be added later, so callers should guard against other + * types being returned. */ + @IconType public int getType() { return mType; } @@ -179,9 +203,13 @@ public final class Icon implements Parcelable { } /** - * @return The package containing resources for this {@link #TYPE_RESOURCE} Icon. - * @hide + * Gets the package used to create this icon. + *

    + * Only valid for icons of type {@link #TYPE_RESOURCE}. + * Note: This package may not be available if referenced in the future, and it is + * up to the caller to ensure safety if this package is re-used and/or persisted. */ + @NonNull public String getResPackage() { if (mType != TYPE_RESOURCE) { throw new IllegalStateException("called getResPackage() on " + this); @@ -190,9 +218,13 @@ public final class Icon implements Parcelable { } /** - * @return The resource ID for this {@link #TYPE_RESOURCE} Icon. - * @hide + * Gets the resource used to create this icon. + *

    + * Only valid for icons of type {@link #TYPE_RESOURCE}. + * Note: This resource may not be available if the application changes at all, and it is + * up to the caller to ensure safety if this resource is re-used and/or persisted. */ + @IdRes public int getResId() { if (mType != TYPE_RESOURCE) { throw new IllegalStateException("called getResId() on " + this); @@ -212,9 +244,13 @@ public final class Icon implements Parcelable { } /** - * @return The {@link android.net.Uri} for this {@link #TYPE_URI} Icon. - * @hide + * Gets the uri used to create this icon. + *

    + * Only valid for icons of type {@link #TYPE_URI}. + * Note: This uri may not be available in the future, and it is + * up to the caller to ensure safety if this uri is re-used and/or persisted. */ + @NonNull public Uri getUri() { return Uri.parse(getUriString()); } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index ded427eb244a2425fafd903314920068091911d3..1924bbe764c7d445c3c6ca7f049b9ba6815e3529 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -783,6 +783,20 @@ public class KeyStore { } } + /** + * Requests keystore to check if the confirmationui HAL is available. + * + * @return whether the confirmationUI HAL is available. + */ + public boolean isConfirmationPromptSupported() { + try { + return mBinder.isConfirmationPromptSupported(); + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return false; + } + } + /** * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error * code. diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk index 1167f76725f1f513f94d5c5f6731e8b637eab776..596e5f5309705fc4c2cc1ca77e9afec1ddc152a7 100644 --- a/keystore/tests/Android.mk +++ b/keystore/tests/Android.mk @@ -24,6 +24,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test LOCAL_PACKAGE_NAME := KeystoreTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_JAVA_LIBRARIES := android.test.runner diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 251b2e773cfb3074031af4d4bd7008de1518807a..70d52164ff7489730b04d3725101d07dab7730eb 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,6 +145,7 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], + static_libs: ["libgmock"], target: { android: { srcs: [ @@ -171,6 +172,7 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", + "tests/AttributeResolution_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index da0205d7212547ff8a7181cca4d0e381f4239ee7..8f58f74d4652fe4ae9c5173893ad112e83e0d2ae 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define ATRACE_TAG ATRACE_TAG_RESOURCES - #include "androidfw/ApkAssets.h" #include @@ -27,7 +25,6 @@ #include "android-base/utf8.h" #include "utils/Compat.h" #include "utils/FileMap.h" -#include "utils/Trace.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" @@ -105,8 +102,6 @@ std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { std::unique_ptr ApkAssets::LoadImpl( unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, std::unique_ptr loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); - ::ZipArchiveHandle unmanaged_handle; int32_t result; if (fd >= 0) { @@ -163,7 +158,6 @@ std::unique_ptr ApkAssets::LoadImpl( } std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - ATRACE_CALL(); CHECK(zip_handle_ != nullptr); ::ZipString name(path.c_str()); @@ -231,12 +225,16 @@ bool ApkAssets::ForEachFile(const std::string& root_path, while ((result = ::Next(cookie, &entry, &name)) == 0) { StringPiece full_file_path(reinterpret_cast(name.name), name.name_length); StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - dirs.insert( - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string()); - } else if (!leaf_file_path.empty()) { - f(leaf_file_path, kFileTypeRegular); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } } } ::EndIteration(cookie); diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 247458d3f4fd7014c7a25b65f3c023a1774ab849..c512a6b06ed1ef979461f9fc26e6472f9c3c1bb4 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index a5698af18f6b2796ba35a3fc624cbef8807dea95..fc625bbaf72d1eeb9eb5ae0f47a8f2d8f238d304 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9af2a372d090be9740763805d9f83..d9f1293183b7fd6654c790f8fa2c3ed24b145408 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,6 +36,31 @@ namespace android { +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -44,6 +69,7 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -74,12 +100,14 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast(package_groups_.size()); package_groups_.push_back({}); - package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id; + DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; + ref_table.mAssignedPackageId = package_id; + ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -94,7 +122,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -105,20 +133,33 @@ void AssetManager2::BuildDynamicRefTable() { void AssetManager2::DumpToLog() const { base::ScopedLogSeverity _log(base::INFO); + LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this); + std::string list; + for (const auto& apk_assets : apk_assets_) { + base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str()); + } + LOG(INFO) << "ApkAssets: " << list; + + list = ""; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + list = ""; + for (const auto& package : package_group.packages_) { + const LoadedPackage* loaded_package = package.loaded_package_; + base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(), + loaded_package->GetPackageId(), + (loaded_package->IsDynamic() ? " dynamic" : "")); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -157,53 +198,55 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { - ATRACE_CALL(); + bool exclude_mipmap) const { + ATRACE_NAME("AssetManager::GetResourceConfigurations"); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { - ATRACE_CALL(); + bool merge_equivalent_languages) const { + ATRACE_NAME("AssetManager::GetResourceLocales"); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { - ATRACE_CALL(); +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { + ATRACE_NAME("AssetManager::OpenDir"); std::string full_path = "assets/" + dirname; std::unique_ptr> files = @@ -236,8 +279,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { - ATRACE_CALL(); + ApkAssetsCookie* out_cookie) const { for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); if (asset) { @@ -255,8 +297,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { - ATRACE_CALL(); + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; } @@ -264,14 +306,13 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -285,55 +326,135 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - FindEntryResult best_entry; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - FindEntryResult current_entry; - for (size_t i = 0; i < package_count; i++) { - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_entry.type_flags; + uint16_t local_entry_idx = entry_idx; - const ResTable_config* current_config = current_entry.config; - const ResTable_config* best_config = best_entry.config; - if (best_cookie == kInvalidCookie || - current_config->isBetterThan(*best_config, desired_config) || - (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { - best_entry = current_entry; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; } } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } + } + } + } + + if (UNLIKELY(best_cookie == kInvalidCookie)) { + return kInvalidCookie; } - if (best_cookie == kInvalidCookie) { + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { return kInvalidCookie; } - *out_entry = best_entry; + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { - ATRACE_CALL(); - +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); @@ -341,7 +462,8 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -369,7 +491,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -383,9 +505,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { - ATRACE_CALL(); - + uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); @@ -402,7 +522,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -414,7 +534,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -422,16 +542,13 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { - ATRACE_CALL(); + uint32_t* out_last_reference) const { constexpr const int kMaxIterations = 20; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); @@ -450,7 +567,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu } const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { - ATRACE_CALL(); + ATRACE_NAME("AssetManager::GetBag"); auto cached_iter = cached_bags_.find(resid); if (cached_iter != cached_bags_.end()) { @@ -492,7 +609,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -524,7 +642,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } @@ -543,7 +662,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -582,7 +702,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -638,7 +759,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -670,7 +791,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -692,6 +814,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -743,7 +891,7 @@ struct Theme::Package { }; bool Theme::ApplyStyle(uint32_t resid, bool force) { - ATRACE_CALL(); + ATRACE_NAME("Theme::ApplyStyle"); const ResolvedBag* bag = asset_manager_->GetBag(resid); if (bag == nullptr) { @@ -872,7 +1020,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9ef121439ce2857e532993be63cf3..f912af4f7190d01e0a9a931624967d7b547aa22e 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res->getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf050d32dfa466b4f41ffca718f480b..04d506a2d71ccd97d99e9ffc79a4c596860a9bf9 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,44 +44,6 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -95,21 +57,22 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -118,7 +81,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, - size_t entry_idx) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } @@ -181,7 +143,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << entry_idx + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; return false; } @@ -191,13 +153,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; return false; } @@ -205,7 +167,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; return false; } @@ -214,12 +176,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; return false; } @@ -228,119 +190,76 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; return false; } } return true; } -bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const { - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { - const Type* type = &type_spec_ptr->types[i]; - const ResTable_type* type_chunk = type->type; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - - // Check if there is the desired entry in this type. - - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { - // No entry found. - continue; - } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - best_offset = uint32_t{dtohs(result->offset)} * 4u; - } else { - if (entry_idx >= entry_count) { - // This entry cannot be here. - continue; - } +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // There is an entry for this resource, record it. - best_offset = offset; - } + // Check if there is the desired entry in this type. - best_config = &type->configuration; - best_type = type_chunk; + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; } - } - if (best_type == nullptr) { - return false; + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; } - if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { - return false; + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; } - const ResTable_entry* best_entry = reinterpret_cast( - reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); - - const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); - out_entry->type_flags = dtohl(flags[entry_idx]); - out_entry->entry = best_entry; - out_entry->config = best_config; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; + const uint32_t* entry_offsets = reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (UNLIKELY(ptr == nullptr)) { - return false; +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } - } - return FindEntry(ptr, entry_idx, config, out_entry); + return reinterpret_cast(reinterpret_cast(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -348,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -369,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -380,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -411,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast(reinterpret_cast(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -433,8 +357,7 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -446,7 +369,7 @@ const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { std::unique_ptr LoadedPackage::Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); + ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr loaded_package(new LoadedPackage()); // typeIdOffset was added at some point, but we still must recognize apps built before this @@ -486,14 +409,10 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); - // A TypeSpec builder. We use this to accumulate the set of Types - // available for a TypeSpec, and later build a single, contiguous block - // of memory that holds all the Types together with the TypeSpec. - std::unique_ptr types_builder; - - // Keep track of the last seen type index. Since type IDs are 1-based, - // this records their index, which is 0-based (type ID - 1). - uint8_t last_type_idx = 0; + // A map of TypeSpec builders, each associated with an type index. + // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, + // contiguous block of memory that holds all the Types together with the TypeSpec. + std::unordered_map> type_builder_map; ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { @@ -525,30 +444,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - ATRACE_NAME("LoadTableTypeSpec"); - - // Starting a new TypeSpec, so finish the old one if there was one. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); - if (type_spec_ptr == nullptr) { - LOG(ERROR) << "Too many type configurations, overflow detected."; - return {}; - } - - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); - } - - types_builder = {}; - last_type_idx = 0; - } - const ResTable_typeSpec* type_spec = child_chunk.header(); if (type_spec == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; @@ -583,8 +478,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } - last_type_idx = type_spec->id - 1; - // If this is an overlay, associate the mapping of this type to the target type // from the IDMAP. const IdmapEntry_header* idmap_entry_header = nullptr; @@ -592,7 +485,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); } - types_builder = util::make_unique(type_spec, idmap_entry_header); + std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; + if (builder_ptr == nullptr) { + builder_ptr = util::make_unique(type_spec, idmap_entry_header); + } else { + LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", + type_spec->id); + } } break; case RES_TABLE_TYPE_TYPE: { @@ -607,12 +506,15 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } // Type chunks must be preceded by their TypeSpec chunks. - if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; + std::unique_ptr& builder_ptr = type_builder_map[type->id - 1]; + if (builder_ptr != nullptr) { + builder_ptr->AddType(type); + } else { + LOG(ERROR) << StringPrintf( + "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", + type->id); return {}; } - - types_builder->AddType(type); } break; case RES_TABLE_LIBRARY_TYPE: { @@ -638,7 +540,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, arraysize(entry_iter->packageName), &package_name); if (dtohl(entry_iter->packageId) >= std::numeric_limits::max()) { - LOG(ERROR) << base::StringPrintf( + LOG(ERROR) << StringPrintf( "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.", dtohl(entry_iter->packageId), package_name.c_str()); return {}; @@ -651,14 +553,20 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } - // Finish the last TypeSpec. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return {}; + } + + // Flatten and construct the TypeSpecs. + for (auto& entry : type_builder_map) { + uint8_t type_idx = static_cast(entry.first); + TypeSpecPtr type_spec_ptr = entry.second->Build(); if (type_spec_ptr == nullptr) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; @@ -669,43 +577,17 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { // If this is an overlay, insert it at the target type ID. if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } } - if (iter.HadError()) { - LOG(ERROR) << iter.GetLastError(); - return {}; - } return std::move(loaded_package); } -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (UNLIKELY(type_id == 0)) { - LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return false; - } - - for (const auto& loaded_package : packages_) { - if (loaded_package->GetPackageId() == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); - } - } - return false; -} - bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { - ATRACE_CALL(); const ResTable_header* header = chunk.header(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; @@ -752,7 +634,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } @@ -767,7 +649,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, std::unique_ptr LoadedArsc::Load(const StringPiece& data, const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); + ATRACE_NAME("LoadedArsc::LoadTable"); // Not using make_unique because the constructor is private. std::unique_ptr loaded_arsc(new LoadedArsc()); @@ -784,7 +666,7 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..23ec5ab0d1f373834639abfd5904d7fce9270dd4 --- /dev/null +++ b/libs/androidfw/OWNERS @@ -0,0 +1,2 @@ +set noparent +toddke@google.com diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 696a00c7b2c033c09103159d8ce4d1a4c1dcdd7c..6268c40f34b4eca9bab89d7b434376698d816a5e 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b033137b47642d864725d7f89ac2e220ab0f6815..ef08897d997a6241be296feac7177cc8066695bc 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,6 +69,8 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -127,7 +129,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -136,24 +138,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname); + std::unique_ptr OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -161,24 +163,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -186,7 +188,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -199,7 +201,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -215,7 +217,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -233,9 +235,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) { + void ForEachPackage(Func func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front()->GetPackageName(), + func(package_group.packages_.front().loaded_package_->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -260,7 +262,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -270,13 +272,43 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector configurations; + std::vector types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector packages_; + // The set of packages that make-up this group. + std::vector packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -350,7 +382,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e745143f0d77211a1bc25ec465eb4f..03fad4947dfeb5c3d98549febfe5a92228e76b6d 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846bf3466260314490f47528c6f4fef..35ef98d8c7046f815ab648641c57caed6bdcf20e 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb26c9b8d8423610e0a4aa9de06cf53..35ae5fcd9e7bbb56fd47c333d5f7a888f426291f 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,32 +41,40 @@ class DynamicPackageEntry { int package_id = 0; }; -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; - - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; - - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast(type_spec + 1); + return flags[entry_index]; + } }; -struct TypeSpec; -class LoadedArsc; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr; class LoadedPackage { public: @@ -76,9 +84,6 @@ class LoadedPackage { ~LoadedPackage(); - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -86,6 +91,12 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -135,14 +146,32 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); - bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const; - ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -152,7 +181,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray> type_specs_; + ByteBucketArray type_specs_; std::vector dynamic_package_map_; }; @@ -180,25 +209,20 @@ class LoadedArsc { return &global_string_pool_; } - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 0000000000000000000000000000000000000000..64924f433245a3bd427e6b4229ada02f2ba98073 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index c2eae855bb7bb54a861edb790d3f6c9fbc2615f5..d94779bf52259c085b000309a690804b7a7b473d 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -28,7 +28,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin StringPiece* out_entry); inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { - return resid | (static_cast(package_id) << 24); + return (resid & 0x00ffffffu) | (static_cast(package_id) << 24); } inline uint8_t get_package_id(uint32_t resid) { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6c43a67e602f2ce8de3ce4f1a732d8f186645dc5..e2b9f0040989a88011c703ab017a339103baf537 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,58 +26,56 @@ using ::android::base::unique_fd; using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -86,19 +84,22 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len)); + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -108,37 +109,30 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); - ASSERT_NE(nullptr, loaded_overlay_apk); + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -146,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 85e8f25394e9e6e7317835fce77140873eabb701..437e14772964855906ca8068e6519aa9e3ff960e 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -81,17 +81,18 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); } -BENCHMARK(BM_AssetManagerGetResource); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); -static void BM_AssetManagerGetResourceOld(benchmark::State& state) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, + state); } -BENCHMARK(BM_AssetManagerGetResourceOld); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -196,7 +197,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - false /*isSystemAssets*/)) { + true /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -211,4 +212,44 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); +static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { + std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.SetConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFramework); + +static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, + true /*isSystemAssets*/)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.setConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); + } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 92462a6cfadf878d704c2a1f18cf1e1499097452..7cac2b3417b57c0efafd75ebe7a6d9aebf0b160e 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -36,6 +36,10 @@ namespace lib_one = com::android::lib_one; namespace lib_two = com::android::lib_two; namespace libclient = com::android::libclient; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::StrEq; + namespace android { class AssetManager2Test : public ::testing::Test { @@ -59,11 +63,14 @@ class AssetManager2Test : public ::testing::Test { libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); ASSERT_NE(nullptr, appaslib_assets_); system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); ASSERT_NE(nullptr, system_assets_); + + app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + ASSERT_THAT(app_assets_, NotNull()); } protected: @@ -75,6 +82,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr libclient_assets_; std::unique_ptr appaslib_assets_; std::unique_ptr system_assets_; + std::unique_ptr app_assets_; }; TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { @@ -228,6 +236,25 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { AssetManager2 assetmanager; + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); + + // First two attributes come from lib_one. + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); +} + +TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { + AssetManager2 assetmanager; + // libclient is built with lib_one and then lib_two in order. // Reverse the order to test that proper package ID re-assignment is happening. assetmanager.SetApkAssets( @@ -446,8 +473,68 @@ TEST_F(AssetManager2Test, GetResourceId) { assetmanager.GetResourceId("main", "layout", "com.android.basic")); } -TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} +TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get()}); + + std::unique_ptr asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER); + ASSERT_THAT(asset, NotNull()); + + const char* data = reinterpret_cast(asset->getBuffer(false /*wordAligned*/)); + ASSERT_THAT(data, NotNull()); + EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n")); +} + +TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()}); + + std::unique_ptr asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER); + ASSERT_THAT(asset, NotNull()); + + const char* data = reinterpret_cast(asset->getBuffer(false /*wordAligned*/)); + ASSERT_THAT(data, NotNull()); + EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n")); +} + +TEST_F(AssetManager2Test, OpenDir) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get()}); + + std::unique_ptr asset_dir = assetmanager.OpenDir(""); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(2u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); -TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} + EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir"))); + EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory)); + + asset_dir = assetmanager.OpenDir("subdir"); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(1u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); +} + +TEST_F(AssetManager2Test, OpenDirFromManyApks) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()}); + + std::unique_ptr asset_dir = assetmanager.OpenDir(""); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(3u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); + + EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt"))); + EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular)); + + EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir"))); + EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory)); +} } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa300c50218aa1be9c857d03898b64f2e9d0062e --- /dev/null +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -0,0 +1,175 @@ +/* + * 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 "benchmark/benchmark.h" + +//#include "android-base/stringprintf.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/AttributeResolution.h" +#include "androidfw/ResourceTypes.h" + +#include "BenchmarkHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace app = com::android::app; +namespace basic = com::android::basic; + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr const static uint32_t Theme_Material_Light = 0x01030237u; + +static void BM_ApplyStyle(benchmark::State& state) { + std::unique_ptr styles_apk = + ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + if (styles_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({styles_apk.get()}); + + std::unique_ptr asset = + assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr theme = assetmanager.NewTheme(); + theme->ApplyStyle(app::R::style::StyleTwo); + + std::array attrs{{app::R::attr::attr_one, app::R::attr::attr_two, + app::R::attr::attr_three, app::R::attr::attr_four, + app::R::attr::attr_five, app::R::attr::attr_empty}}; + std::array values; + std::array indices; + + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyle); + +static void BM_ApplyStyleFramework(benchmark::State& state) { + std::unique_ptr framework_apk = ApkAssets::Load(kFrameworkPath); + if (framework_apk == nullptr) { + state.SkipWithError("failed to load framework assets"); + return; + } + + std::unique_ptr basic_apk = + ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + if (basic_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); + + ResTable_config device_config; + memset(&device_config, 0, sizeof(device_config)); + device_config.language[0] = 'e'; + device_config.language[1] = 'n'; + device_config.country[0] = 'U'; + device_config.country[1] = 'S'; + device_config.orientation = ResTable_config::ORIENTATION_PORT; + device_config.smallestScreenWidthDp = 700; + device_config.screenWidthDp = 700; + device_config.screenHeightDp = 1024; + device_config.sdkVersion = 27; + + Res_value value; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, + 0u /*density_override*/, &value, &config, &flags); + if (cookie == kInvalidCookie) { + state.SkipWithError("failed to find R.layout.layout"); + return; + } + + size_t len = 0u; + const char* layout_path = + assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); + if (layout_path == nullptr || len == 0u) { + state.SkipWithError("failed to lookup layout path"); + return; + } + + std::unique_ptr asset = assetmanager.OpenNonAsset( + StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr theme = assetmanager.NewTheme(); + theme->ApplyStyle(Theme_Material_Light); + + std::array attrs{ + {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, + 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, + 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, + 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, + 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, + 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, + 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, + 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, + 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, + 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, + 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, + 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, + 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, + 0x011100ca}}; + + std::array values; + std::array indices; + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyleFramework); + +} // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3973087702bab0129f3e283050a90..c8dbe205fee244466f38ea5456e259a5b4a59ae5 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,8 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceUtils.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +34,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +49,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,15 +65,50 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { ResXMLTree xml_parser_; }; +TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { + AssetManager2 assetmanager; + auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, apk_assets); + assetmanager.SetApkAssets({apk_assets.get()}); + + std::unique_ptr theme = assetmanager.NewTheme(); + + std::array attrs{ + {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; + std::array values; + std::array indices; + ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), + indices.data()); + + const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; + + const uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(1u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(2u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +161,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +206,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797fbf15db1d3021335aee8ac6f69659..faddfe599af48374d9ea7d6508121877b84c852c 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,19 +33,21 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_ref = 0u; while (state.KeepRunning()) { - table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); + ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); + table.resolveReference(&value, block, &last_ref, &flags, &selected_config); } } @@ -72,10 +74,12 @@ void GetResourceBenchmark(const std::vector& paths, const ResTable_ Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_id = 0u; while (state.KeepRunning()) { - assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); + ApkAssetsCookie cookie = assetmanager.GetResource( + resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); } } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 37ddafb14fd3517cb25f8dc29c85e9624f66070a..cae632ddea30018bff26537fd469de9dfff377be 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,9 @@ #include "androidfw/LoadedArsc.h" +#include "android-base/file.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -27,6 +30,14 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::android::base::ReadFileToString; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -35,39 +46,24 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); - - const std::vector>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; + ASSERT_THAT(loaded_arsc, NotNull()); - FindEntryResult entry; - - ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); - ASSERT_NE(nullptr, entry.entry); -} - -TEST(LoadedArscTest, FindDefaultEntry) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,15 +72,22 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); + + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -93,14 +96,13 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,25 +116,23 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -143,13 +143,12 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -157,21 +156,67 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); - EXPECT_EQ(std::string("string"), type_name); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); +} + +// AAPT(2) generates resource tables with chunks in a certain order. The rule is that +// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with +// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however. +// +// AAPT(2) generates something like: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=2 +// +// But the following is valid too: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=2 +// +TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", + "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); + + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + const auto& package = loaded_arsc->GetPackages()[0]; + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + type_spec = package->GetTypeSpecByTypeIndex(1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); } class MockLoadedIdmap : public LoadedIdmap { @@ -199,23 +244,33 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents, overlay_contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &overlay_contents)); + &contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); - ASSERT_NE(nullptr, loaded_arsc); - - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 43a995536d89e796894e4e6ac9e7aaa1bd68379a..df0c642f45653ff008a711e846053179517ce43b 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,6 +20,7 @@ #include #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk index ccb08242a656e387a1e2c914dd99e2119c746edf..c8ad86ded8519b81b604adf03a37489b422356a3 100644 Binary files a/libs/androidfw/tests/data/app/app.apk and b/libs/androidfw/tests/data/app/app.apk differ diff --git a/libs/androidfw/tests/data/app/assets/app_file.txt b/libs/androidfw/tests/data/app/assets/app_file.txt new file mode 100644 index 0000000000000000000000000000000000000000..b214e06d6ece9f2719d66e8b8664e01d82520754 --- /dev/null +++ b/libs/androidfw/tests/data/app/assets/app_file.txt @@ -0,0 +1 @@ +app file diff --git a/libs/androidfw/tests/data/app/assets/file.txt b/libs/androidfw/tests/data/app/assets/file.txt new file mode 100644 index 0000000000000000000000000000000000000000..0811542725207767109ac22958aacb1d37e7900c --- /dev/null +++ b/libs/androidfw/tests/data/app/assets/file.txt @@ -0,0 +1 @@ +app override file diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build index d418158c547b1ff31f00e108d5b12ded8856b0a9..09af842e70fb6b28a15bb98bacd7595e7c65d02c 100755 --- a/libs/androidfw/tests/data/app/build +++ b/libs/androidfw/tests/data/app/build @@ -17,4 +17,11 @@ set -e -aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f +aapt2 compile --dir res -o compiled.flata +aapt2 link \ + --manifest AndroidManifest.xml \ + -I ../system/system.apk \ + -A assets \ + -o app.apk \ + compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 94a2a14ced8786acfa1b0d410c9ffb9f12f0c785..b7e814fea079be22b311bec292c6524ce21d32ed 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,6 +34,7 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, + layoutt = 0x7f020001, }; }; @@ -55,6 +56,7 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, + deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk index 18ef75e91dedc0c9fbb63bfc7b0b0a34b0782af7..1733b6a1654645caf190982b31abe9255a554e35 100644 Binary files a/libs/androidfw/tests/data/basic/basic.apk and b/libs/androidfw/tests/data/basic/basic.apk differ diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml new file mode 100644 index 0000000000000000000000000000000000000000..045ede454bca3b2d46e18cc5d1eb614d5f98ef53 --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml @@ -0,0 +1,25 @@ + + +