Merge "AudioService: makes some members accessible by a derived class"
diff --git a/Android.bp b/Android.bp
index d5e04f9..c5d64fb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -373,7 +373,6 @@
"core/java/com/android/internal/appwidget/IAppWidgetHost.aidl",
"core/java/com/android/internal/backup/IBackupTransport.aidl",
"core/java/com/android/internal/backup/IObbBackupService.aidl",
- "core/java/com/android/internal/car/ICarServiceHelper.aidl",
"core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl",
"core/java/com/android/internal/net/INetworkWatchlistManager.aidl",
"core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl",
@@ -579,7 +578,6 @@
"wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
"wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
"wifi/java/android/net/wifi/IWifiScanner.aidl",
- "wifi/java/android/net/wifi/IRttManager.aidl",
"packages/services/PacProcessor/com/android/net/IProxyService.aidl",
"packages/services/Proxy/com/android/net/IProxyCallback.aidl",
"packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
diff --git a/Android.mk b/Android.mk
index d72e398..a78a01a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -827,35 +827,35 @@
# ==== hiddenapi lists =======================================
-# Copy blacklist and dark greylist over into the build folder.
+# Copy blacklist and light greylist over into the build folder.
# This is for ART buildbots which need to mock these lists and have alternative
# rules for building them. Other rules in the build system should depend on the
# files in the build folder.
$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-light-greylist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)))
-# Generate light greylist as private API minus (blacklist plus dark greylist).
+# Generate dark greylist as private API minus (blacklist plus light greylist).
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
- if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
- echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+ if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+ echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
exit 1; \
elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
exit 2; \
- elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
- echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
+ echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
exit 3; \
fi
- comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@
+ comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
# Include subdirectory makefiles
# ============================================================
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
new file mode 100644
index 0000000..f8fd51d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.StubActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// Due to b/71353150, you might get "java.lang.AssertionError: Binder ProxyMap has too many
+// entries", but it's flaky. Adding "Runtime.getRuntime().gc()" between each iteration solves
+// the problem, but it doesn't seem like it's currently needed.
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PendingIntentPerfTest {
+
+ private Context mContext;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Intent mIntent;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mIntent = StubActivity.createLaunchIntent(mContext);
+ }
+
+ /**
+ * Benchmark time to create a PendingIntent.
+ */
+ @Test
+ public void create() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ state.resumeTiming();
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ 0);
+
+ state.pauseTiming();
+ pendingIntent.cancel();
+ state.resumeTiming();
+ }
+ }
+
+ /**
+ * Benchmark time to create a PendingIntent with FLAG_CANCEL_CURRENT, already having an active
+ * PendingIntent.
+ */
+ @Test
+ public void createWithCancelFlag() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
+ mIntent, 0);
+ state.resumeTiming();
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ state.pauseTiming();
+ pendingIntent.cancel();
+ state.resumeTiming();
+ }
+ }
+
+ /**
+ * Benchmark time to create a PendingIntent with FLAG_UPDATE_CURRENT, already having an active
+ * PendingIntent.
+ */
+ @Test
+ public void createWithUpdateFlag() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
+ mIntent, 0);
+ state.resumeTiming();
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ state.pauseTiming();
+ previousPendingIntent.cancel();
+ pendingIntent.cancel();
+ state.resumeTiming();
+ }
+ }
+
+ /**
+ * Benchmark time to cancel a PendingIntent.
+ */
+ @Test
+ public void cancel() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
+ mIntent, 0);
+ state.resumeTiming();
+
+ pendingIntent.cancel();
+ }
+ }
+}
+
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
new file mode 100644
index 0000000..60c6d89
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class StaticLayoutMultithreadPerfTest {
+ private static final int WORD_LENGTH = 9; // Random word has 9 characters.
+ private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
+ private static final boolean NO_STYLE_TEXT = false;
+
+ private static TextPaint PAINT = new TextPaint();
+ private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+ public StaticLayoutMultithreadPerfTest() {}
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private CountDownLatch mStartLatch;
+ private AtomicBoolean mThreadState; // True for running, False for stopped.
+
+ private static final long TIMEOUT_MS = 5000;
+
+ private Thread[] startBackgroundThread(int numOfThreads) {
+ mStartLatch = new CountDownLatch(numOfThreads);
+ mThreadState = new AtomicBoolean(true);
+
+ Thread[] threads = new Thread[numOfThreads];
+ for (int i = 0; i < numOfThreads; ++i) {
+ final int seed = i + 1;
+ threads[i] = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final TextPerfUtils util = new TextPerfUtils();
+ util.resetRandom(seed);
+
+ mStartLatch.countDown();
+ while (mThreadState.get()) {
+ final CharSequence text = util.nextRandomParagraph(
+ WORD_LENGTH, NO_STYLE_TEXT);
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .build();
+ }
+ }
+ });
+ }
+
+ for (int i = 0; i < numOfThreads; ++i) {
+ threads[i].start();
+ }
+
+ try {
+ mStartLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return threads;
+ }
+
+ private void finishThreads(Thread[] threads) {
+ mThreadState.set(false);
+ for (Thread thread : threads) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ mStartLatch = null;
+ mThreadState = null;
+ }
+
+ private void runRandomTest(int numOfTotalThreads) {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final TextPerfUtils util = new TextPerfUtils();
+ Thread[] threads = startBackgroundThread(numOfTotalThreads - 1);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final CharSequence text = util.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .build();
+ }
+ finishThreads(threads);
+ }
+
+ @Test
+ public void testCreate_RandomText_Thread_1() {
+ runRandomTest(1);
+ }
+
+ @Test
+ public void testCreate_RandomText_Thread_2() {
+ runRandomTest(2);
+ }
+
+ @Test
+ public void testCreate_RandomText_Thread_4() {
+ runRandomTest(4);
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index bab2a85..231aaf2 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -232,7 +232,7 @@
while (state.keepRunning()) {
state.pauseTiming();
final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
.build();
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
index 6012f4b1..8f03f7e 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
@@ -17,6 +17,14 @@
package android.perftests.utils;
import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
public class StubActivity extends Activity {
-}
\ No newline at end of file
+ public static Intent createLaunchIntent(Context context) {
+ final Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(context, StubActivity.class);
+ return intent;
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index bbe3c49..9204289 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -902,6 +902,7 @@
field public static final int maxLength = 16843104; // 0x1010160
field public static final int maxLevel = 16843186; // 0x10101b2
field public static final int maxLines = 16843091; // 0x1010153
+ field public static final int maxLongVersionCode = 16844163; // 0x1010583
field public static final int maxRecents = 16843846; // 0x1010446
field public static final int maxRows = 16843059; // 0x1010133
field public static final int maxSdkVersion = 16843377; // 0x1010271
@@ -5710,7 +5711,8 @@
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
field public static final java.lang.String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
- field public static final java.lang.String EXTRA_BLOCK_STATE_CHANGED_ID = "android.app.extra.BLOCK_STATE_CHANGED_ID";
+ field public static final java.lang.String EXTRA_NOTIFICATION_CHANNEL_GROUP_ID = "android.app.extra.NOTIFICATION_CHANNEL_GROUP_ID";
+ field public static final java.lang.String EXTRA_NOTIFICATION_CHANNEL_ID = "android.app.extra.NOTIFICATION_CHANNEL_ID";
field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
field public static final int IMPORTANCE_HIGH = 4; // 0x4
field public static final int IMPORTANCE_LOW = 2; // 0x2
@@ -6491,7 +6493,6 @@
method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
method public boolean isOverrideApnEnabled(android.content.ComponentName);
method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public boolean isPrintingEnabled();
method public boolean isProfileOwnerApp(java.lang.String);
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isResetPasswordTokenActive(android.content.ComponentName);
@@ -6564,7 +6565,6 @@
method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
- method public void setPrintingEnabled(android.content.ComponentName, boolean);
method public void setProfileEnabled(android.content.ComponentName);
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -6739,6 +6739,7 @@
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
+ field public static final int TAG_CRYPTO_SELF_TEST_COMPLETED = 210031; // 0x3346f
field public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET = 210021; // 0x33465
field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
@@ -11616,15 +11617,15 @@
public final class AssetManager implements java.lang.AutoCloseable {
method public void close();
- method public final java.lang.String[] getLocales();
- method public final java.lang.String[] list(java.lang.String) throws java.io.IOException;
- method public final java.io.InputStream open(java.lang.String) throws java.io.IOException;
- method public final java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
- method public final android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
- method public final android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
- method public final android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
- method public final android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
- method public final android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
+ method public java.lang.String[] getLocales();
+ method public java.lang.String[] list(java.lang.String) throws java.io.IOException;
+ method public java.io.InputStream open(java.lang.String) throws java.io.IOException;
+ method public java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
+ method public android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
+ method public android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
+ method public android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
+ method public android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
+ method public android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
field public static final int ACCESS_BUFFER = 3; // 0x3
field public static final int ACCESS_RANDOM = 1; // 0x1
field public static final int ACCESS_STREAMING = 2; // 0x2
@@ -13048,6 +13049,8 @@
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
method public int describeContents();
method public void eraseColor(int);
@@ -14105,6 +14108,7 @@
method public void endRecording();
method public int getHeight();
method public int getWidth();
+ method public boolean requiresHardwareAcceleration();
method public deprecated void writeToStream(java.io.OutputStream);
}
@@ -14600,7 +14604,7 @@
method public final android.graphics.Rect copyBounds();
method public static android.graphics.drawable.Drawable createFromPath(java.lang.String);
method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String);
- method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options);
+ method public static deprecated android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options);
method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String);
method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -16291,7 +16295,7 @@
public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
method public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults();
- method public <T> T getPhysicalCameraKey(android.hardware.camera2.CaptureResult.Key<T>, java.lang.String);
+ method public java.util.Map<java.lang.String, android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
}
}
@@ -16486,8 +16490,37 @@
package android.hardware.fingerprint {
public class FingerprintDialog {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
- method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
+ method public void authenticate(android.hardware.fingerprint.FingerprintDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
+ method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
+ field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+ field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
+ field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
+ field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
+ field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
+ }
+
+ public static abstract class FingerprintDialog.AuthenticationCallback {
+ ctor public FingerprintDialog.AuthenticationCallback();
+ method public void onAuthenticationError(int, java.lang.CharSequence);
+ method public void onAuthenticationFailed();
+ method public void onAuthenticationHelp(int, java.lang.CharSequence);
+ method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintDialog.AuthenticationResult);
+ }
+
+ public static class FingerprintDialog.AuthenticationResult {
+ method public android.hardware.fingerprint.FingerprintDialog.CryptoObject getCryptoObject();
}
public static class FingerprintDialog.Builder {
@@ -16499,10 +16532,19 @@
method public android.hardware.fingerprint.FingerprintDialog.Builder setTitle(java.lang.CharSequence);
}
- public class FingerprintManager {
- method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
- method public boolean hasEnrolledFingerprints();
- method public boolean isHardwareDetected();
+ public static final class FingerprintDialog.CryptoObject {
+ ctor public FingerprintDialog.CryptoObject(java.security.Signature);
+ ctor public FingerprintDialog.CryptoObject(javax.crypto.Cipher);
+ ctor public FingerprintDialog.CryptoObject(javax.crypto.Mac);
+ method public javax.crypto.Cipher getCipher();
+ method public javax.crypto.Mac getMac();
+ method public java.security.Signature getSignature();
+ }
+
+ public deprecated class FingerprintManager {
+ method public deprecated void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
+ method public deprecated boolean hasEnrolledFingerprints();
+ method public deprecated boolean isHardwareDetected();
field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16510,9 +16552,11 @@
field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
@@ -21946,6 +21990,7 @@
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
+ field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
@@ -22150,6 +22195,20 @@
field public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
+ public final class AudioPresentation {
+ method public java.util.Map<java.util.Locale, java.lang.String> getLabels();
+ method public java.util.Locale getLocale();
+ method public int getMasteringIndication();
+ method public boolean hasAudioDescription();
+ method public boolean hasDialogueEnhancement();
+ method public boolean hasSpokenSubtitles();
+ field public static final int MASTERED_FOR_3D = 3; // 0x3
+ field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
+ field public static final int MASTERED_FOR_STEREO = 1; // 0x1
+ field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
+ field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
+ }
+
public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -22316,6 +22375,7 @@
method public int setPlaybackRate(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ method public int setPresentation(android.media.AudioPresentation);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -23472,6 +23532,7 @@
ctor public MediaExtractor();
method public boolean advance();
method protected void finalize();
+ method public java.util.List<android.media.AudioPresentation> getAudioPresentations(int);
method public long getCachedDuration();
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
@@ -24498,7 +24559,7 @@
field public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; // 0x4
field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
- field public static final int DIRECTIONALITY_UNKNOW = 0; // 0x0
+ field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0
field public static final int LOCATION_MAINBODY = 1; // 0x1
field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
field public static final int LOCATION_PERIPHERAL = 3; // 0x3
@@ -28586,10 +28647,14 @@
field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
field public static final java.lang.String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
+ field public static final java.lang.String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED";
field public static final java.lang.String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
+ field public static final java.lang.String EXTRA_AID = "android.nfc.extra.AID";
+ field public static final java.lang.String EXTRA_DATA = "android.nfc.extra.DATA";
field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
+ field public static final java.lang.String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
field public static final int FLAG_READER_NFC_A = 1; // 0x1
field public static final int FLAG_READER_NFC_B = 2; // 0x2
@@ -33055,7 +33120,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
- field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCATION = "no_config_location";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
@@ -33074,6 +33139,7 @@
field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+ field public static final java.lang.String DISALLOW_PRINTING = "no_printing";
field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
@@ -40439,6 +40505,7 @@
public final class Call {
method public void answer(int);
method public void conference(android.telecom.Call);
+ method public void deflect(android.net.Uri);
method public void disconnect();
method public java.util.List<java.lang.String> getCannedTextResponses();
method public java.util.List<android.telecom.Call> getChildren();
@@ -40549,6 +40616,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
@@ -40696,6 +40764,7 @@
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
+ method public void onDeflect(android.net.Uri);
method public void onDisconnect();
method public void onExtrasChanged(android.os.Bundle);
method public void onHandoverComplete();
@@ -40764,6 +40833,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 33554432; // 0x2000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
@@ -41746,10 +41816,10 @@
method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
method public int download(android.telephony.mbms.DownloadRequest);
- method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public java.io.File getTempFileRootDirectory();
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public void requestUpdateFileServices(java.util.List<java.lang.String>);
method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
method public void setTempFileRootDirectory(java.io.File);
@@ -42678,6 +42748,7 @@
public static class MbmsErrors.DownloadErrors {
field public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; // 0x191
field public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; // 0x192
+ field public static final int ERROR_UNKNOWN_FILE_INFO = 403; // 0x193
}
public static class MbmsErrors.GeneralErrors {
@@ -44911,6 +44982,7 @@
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_420 = 420; // 0x1a4
+ field public static final int DENSITY_440 = 440; // 0x1b8
field public static final int DENSITY_560 = 560; // 0x230
field public static final int DENSITY_DEFAULT = 160; // 0xa0
field public static final int DENSITY_DEVICE_STABLE;
@@ -47369,6 +47441,7 @@
method public boolean isTextDirectionResolved();
method public boolean isVerticalFadingEdgeEnabled();
method public boolean isVerticalScrollBarEnabled();
+ method public boolean isVisibleToUserForAutofill(int);
method public void jumpDrawablesToCurrentState();
method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
method public void layout(int, int, int, int);
@@ -48078,8 +48151,6 @@
method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect);
method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect);
- method protected void onDebugDraw(android.graphics.Canvas);
- method protected void onDebugDrawMargins(android.graphics.Canvas, android.graphics.Paint);
method public boolean onInterceptHoverEvent(android.view.MotionEvent);
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
@@ -48153,7 +48224,6 @@
ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet);
ctor public ViewGroup.LayoutParams(int, int);
ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams);
- method public void onDebugDraw(android.view.View, android.graphics.Canvas, android.graphics.Paint);
method public void resolveLayoutDirection(int);
method protected void setBaseAttributes(android.content.res.TypedArray, int, int);
field public static final deprecated int FILL_PARENT = -1; // 0xffffffff
diff --git a/api/system-current.txt b/api/system-current.txt
index 87269d0..6a6d667 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29,6 +29,8 @@
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
+ field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
+ field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
@@ -360,10 +362,12 @@
public final class StatsManager {
method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
+ method public boolean addConfiguration(long, byte[]);
method public byte[] getData(long);
method public byte[] getMetadata();
method public boolean removeConfiguration(long);
method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
+ method public boolean setDataFetchOperation(long, android.app.PendingIntent);
field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
field public static final java.lang.String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
@@ -685,6 +689,12 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
}
+ public static final class UsageEvents.Event {
+ method public int getStandbyBucket();
+ field public static final int NOTIFICATION_SEEN = 10; // 0xa
+ field public static final int STANDBY_BUCKET_CHANGED = 11; // 0xb
+ }
+
public final class UsageStatsManager {
method public int getAppStandbyBucket(java.lang.String);
method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
@@ -773,6 +783,7 @@
public abstract class Context {
method public boolean bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
method public abstract android.content.Context createCredentialProtectedStorageContext();
+ method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.io.File getPreloadsFileCache();
method public abstract boolean isCredentialProtectedStorage();
method public abstract void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
@@ -788,7 +799,7 @@
field public static final java.lang.String STATS_MANAGER = "stats";
field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final java.lang.String VR_SERVICE = "vrmanager";
- field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
+ field public static final deprecated java.lang.String WIFI_RTT_SERVICE = "rttmanager";
field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
}
@@ -1140,6 +1151,15 @@
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -1147,11 +1167,14 @@
field public final float batteryLevel;
field public final float brightness;
field public final int colorTemperature;
+ field public final boolean isDefaultBrightnessConfig;
+ field public final boolean isUserSetBrightness;
field public final float lastBrightness;
field public final long[] luxTimestamps;
field public final float[] luxValues;
field public final boolean nightMode;
field public final java.lang.String packageName;
+ field public final float powerBrightnessFactor;
field public final long timeStamp;
}
@@ -2009,6 +2032,7 @@
method public int describeContents();
method public android.hardware.radio.RadioManager.BandDescriptor[] getBands();
method public int getClassId();
+ method public java.util.Map<java.lang.String, java.lang.Integer> getDabFrequencyTable();
method public int getId();
method public java.lang.String getImplementor();
method public int getNumAudioSources();
@@ -2500,6 +2524,7 @@
method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method public deprecated int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method public int requestAudioFocus(android.media.AudioFocusRequest, android.media.audiopolicy.AudioPolicy);
+ method public void setFocusRequestResult(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
method public void unregisterAudioPolicyAsync(android.media.audiopolicy.AudioPolicy);
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
@@ -3074,7 +3099,7 @@
package android.net.wifi {
- public class RttManager {
+ public deprecated class RttManager {
method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities();
@@ -3150,22 +3175,22 @@
field public int supportedType;
}
- public static class RttManager.ParcelableRttParams implements android.os.Parcelable {
+ public static deprecated class RttManager.ParcelableRttParams implements android.os.Parcelable {
field public android.net.wifi.RttManager.RttParams[] mParams;
}
- public static class RttManager.ParcelableRttResults implements android.os.Parcelable {
+ public static deprecated class RttManager.ParcelableRttResults implements android.os.Parcelable {
ctor public RttManager.ParcelableRttResults(android.net.wifi.RttManager.RttResult[]);
field public android.net.wifi.RttManager.RttResult[] mResults;
}
- public static abstract class RttManager.ResponderCallback {
+ public static abstract deprecated class RttManager.ResponderCallback {
ctor public RttManager.ResponderCallback();
method public abstract void onResponderEnableFailure(int);
method public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig);
}
- public static class RttManager.ResponderConfig implements android.os.Parcelable {
+ public static deprecated class RttManager.ResponderConfig implements android.os.Parcelable {
ctor public RttManager.ResponderConfig();
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -3178,7 +3203,7 @@
field public int preamble;
}
- public static class RttManager.RttCapabilities implements android.os.Parcelable {
+ public static deprecated class RttManager.RttCapabilities implements android.os.Parcelable {
ctor public RttManager.RttCapabilities();
field public int bwSupported;
field public boolean lciSupported;
@@ -3193,13 +3218,13 @@
field public boolean twoSided11McRttSupported;
}
- public static abstract interface RttManager.RttListener {
+ public static abstract deprecated interface RttManager.RttListener {
method public abstract void onAborted();
method public abstract void onFailure(int, java.lang.String);
method public abstract void onSuccess(android.net.wifi.RttManager.RttResult[]);
}
- public static class RttManager.RttParams {
+ public static deprecated class RttManager.RttParams {
ctor public RttManager.RttParams();
field public boolean LCIRequest;
field public boolean LCRRequest;
@@ -3223,7 +3248,7 @@
field public boolean secure;
}
- public static class RttManager.RttResult {
+ public static deprecated class RttManager.RttResult {
ctor public RttManager.RttResult();
field public android.net.wifi.RttManager.WifiInformationElement LCI;
field public android.net.wifi.RttManager.WifiInformationElement LCR;
@@ -3260,7 +3285,7 @@
field public deprecated int tx_rate;
}
- public static class RttManager.WifiInformationElement {
+ public static deprecated class RttManager.WifiInformationElement {
ctor public RttManager.WifiInformationElement();
field public byte[] data;
field public byte id;
@@ -4106,6 +4131,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+ field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
@@ -5017,22 +5043,19 @@
}
public class UiccSlotInfo implements android.os.Parcelable {
- ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int);
+ ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
method public int describeContents();
method public java.lang.String getCardId();
method public int getCardStateInfo();
method public boolean getIsActive();
method public boolean getIsEuicc();
+ method public int getLogicalSlotIdx();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
field public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
- field public final java.lang.String cardId;
- field public final int cardStateInfo;
- field public final boolean isActive;
- field public final boolean isEuicc;
}
public abstract class VisualVoicemailService extends android.app.Service {
@@ -5693,6 +5716,7 @@
ctor public ImsCallSessionImplBase();
method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
method public void close();
+ method public void deflect(java.lang.String);
method public void extendToConference(java.lang.String[]);
method public java.lang.String getCallId();
method public android.telephony.ims.ImsCallProfile getCallProfile();
@@ -5887,11 +5911,11 @@
method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public void dispose(int) throws android.os.RemoteException;
method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index bf00343..b02da04 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -183,10 +183,7 @@
}
public abstract class Context {
- method public abstract int getUserId();
- }
-
- public class ContextWrapper extends android.content.Context {
+ method public android.os.UserHandle getUser();
method public int getUserId();
}
@@ -214,6 +211,7 @@
method public abstract boolean isPermissionReviewModeEnabled();
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
+ field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
}
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -298,6 +296,15 @@
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -305,11 +312,14 @@
field public final float batteryLevel;
field public final float brightness;
field public final int colorTemperature;
+ field public final boolean isDefaultBrightnessConfig;
+ field public final boolean isUserSetBrightness;
field public final float lastBrightness;
field public final long[] luxTimestamps;
field public final float[] luxValues;
field public final boolean nightMode;
field public final java.lang.String packageName;
+ field public final float powerBrightnessFactor;
field public final long timeStamp;
}
@@ -740,11 +750,11 @@
method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public void dispose(int) throws android.os.RemoteException;
method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
@@ -1028,6 +1038,7 @@
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
+ method public void writeToParcelNoRecycle(android.os.Parcel, int);
}
public final class AccessibilityWindowInfo implements android.os.Parcelable {
@@ -1044,6 +1055,14 @@
}
+package android.view.inputmethod {
+
+ public final class InputMethodManager {
+ method public boolean isInputMethodPickerShown();
+ }
+
+}
+
package android.widget {
public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
@@ -1060,6 +1079,12 @@
field public static final int MODE_SPINNER = 1; // 0x1
}
+ public final class Magnifier {
+ method public android.graphics.Bitmap getContent();
+ method public static android.graphics.PointF getMagnifierDefaultSize();
+ method public android.graphics.Rect getWindowPositionOnScreen();
+ }
+
public class NumberPicker extends android.widget.LinearLayout {
method public java.lang.CharSequence getDisplayedValueForCurrentSelection();
}
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index f75678b..6e0bd3a 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -34,6 +35,7 @@
import libcore.io.Streams;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -583,7 +585,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
- Streams.copy(new FileInputStream(fd.getFileDescriptor()), System.out);
+ FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
}
@@ -596,7 +598,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
- Streams.copy(System.in, new FileOutputStream(fd.getFileDescriptor()));
+ FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
}
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 47581e1..7c1a555 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -46,6 +46,7 @@
private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+ private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
@@ -76,11 +77,15 @@
"\n" +
"dpm remove-active-admin: Disables an active admin, the admin must have declared" +
" android:testOnly in the application in its manifest. This will also remove" +
- " device and profile owners\n" +
+ " device and profile owners.\n" +
"\n" +
"dpm " + COMMAND_CLEAR_FREEZE_PERIOD_RECORD + ": clears framework-maintained " +
"record of past freeze periods that the device went through. For use during " +
- "feature development to prevent triggering restriction on setting freeze periods");
+ "feature development to prevent triggering restriction on setting freeze " +
+ "periods.\n" +
+ "\n" +
+ "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
+ "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
}
@Override
@@ -109,11 +114,26 @@
case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
runClearFreezePeriodRecord();
break;
+ case COMMAND_FORCE_SECURITY_LOGS:
+ runForceSecurityLogs();
+ break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
}
+ private void runForceSecurityLogs() throws RemoteException, InterruptedException {
+ while (true) {
+ final long toWait = mDevicePolicyManager.forceSecurityLogs();
+ if (toWait == 0) {
+ break;
+ }
+ System.out.println("We have to wait for " + toWait + " milliseconds...");
+ Thread.sleep(toWait);
+ }
+ System.out.println("Success");
+ }
+
private void parseArgs(boolean canHaveName) {
String opt;
while ((opt = nextOption()) != null) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 1d5ab59..654036e 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -46,6 +46,7 @@
checkIncidentPermissions(const IncidentReportArgs& args)
{
uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
// root doesn't have permission.DUMP if don't do this!
return Status::ok();
@@ -54,13 +55,13 @@
// checking calling permission.
if (!checkCallingPermission(DUMP_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
- IPCThreadState::self()->getCallingPid(), callingUid);
+ callingPid, callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.DUMP");
}
if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
- IPCThreadState::self()->getCallingPid(), callingUid);
+ callingPid, callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.USAGE_STATS");
}
@@ -68,13 +69,17 @@
// checking calling request uid permission.
switch (args.dest()) {
case DEST_LOCAL:
- if (callingUid != AID_SHELL || callingUid != AID_ROOT) {
+ if (callingUid != AID_SHELL && callingUid != AID_ROOT) {
+ ALOGW("Calling pid %d and uid %d does not have permission to get local data.",
+ callingPid, callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission to get local data.");
}
case DEST_EXPLICIT:
- if (callingUid != AID_SHELL || callingUid != AID_ROOT ||
- callingUid != AID_STATSD || callingUid != AID_SYSTEM) {
+ if (callingUid != AID_SHELL && callingUid != AID_ROOT &&
+ callingUid != AID_STATSD && callingUid != AID_SYSTEM) {
+ ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.",
+ callingPid, callingUid);
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission to get explicit data.");
}
diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
index 360b230..b76d669 100644
--- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
+++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
@@ -19,6 +19,7 @@
import android.accounts.Account;
import android.content.ContentResolver;
+import android.content.SyncRequest;
import android.os.Bundle;
import java.net.URISyntaxException;
@@ -28,12 +29,31 @@
private String[] mArgs;
private int mNextArg;
private String mCurArgData;
+ private boolean mIsForegroundRequest;
enum Operation {
REQUEST_SYNC {
@Override
void invoke(RequestSync caller) {
- ContentResolver.requestSync(caller.mAccount, caller.mAuthority, caller.mExtras);
+ if (caller.mIsForegroundRequest) {
+ caller.mExtras.putBoolean(
+ ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC, true);
+ } else {
+ caller.mExtras.putBoolean(
+ ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC, true);
+ System.out.println(
+ "Making a sync request as a background app.\n"
+ + "Note: request may be throttled by App Standby.\n"
+ + "To override this behavior and run a sync immediately,"
+ + " pass a -f option.\n");
+ }
+ final SyncRequest request =
+ new SyncRequest.Builder()
+ .setSyncAdapter(caller.mAccount, caller.mAuthority)
+ .setExtras(caller.mExtras)
+ .syncOnce()
+ .build();
+ ContentResolver.requestSync(request);
}
},
ADD_PERIODIC_SYNC {
@@ -191,6 +211,10 @@
final String key = nextArgRequired();
final String value = nextArgRequired();
mExtras.putBoolean(key, Boolean.valueOf(value));
+
+ } else if (opt.equals("-f") || opt.equals("--foreground")) {
+ mIsForegroundRequest = true;
+
} else {
System.err.println("Error: Unknown option: " + opt);
showUsage();
@@ -267,6 +291,9 @@
" -n|--account-name <ACCOUNT-NAME>\n" +
" -t|--account-type <ACCOUNT-TYPE>\n" +
" -a|--authority <AUTHORITY>\n" +
+ " App-standby related options\n" +
+ "\n" +
+ " -f|--foreground (Exempt a sync from app standby)\n" +
" ContentResolver extra options:\n" +
" --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
" --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index eabbb96..90158a0 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -38,9 +38,13 @@
src/external/StatsPuller.cpp \
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/StatsPullerManagerImpl.cpp \
+ src/external/puller_util.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
src/logd/LogReader.cpp \
@@ -62,6 +66,7 @@
src/storage/StorageManager.cpp \
src/StatsLogProcessor.cpp \
src/StatsService.cpp \
+ src/subscriber/IncidentdReporter.cpp \
src/subscriber/SubscriberReporter.cpp \
src/HashableDimensionKey.cpp \
src/guardrail/MemoryLeakTrackUtil.cpp \
@@ -75,7 +80,8 @@
$(LOCAL_PATH)/../../core/java
statsd_common_static_libraries := \
- libplatformprotos
+ libhealthhalutils \
+ libplatformprotos \
statsd_common_shared_libraries := \
libbase \
@@ -93,6 +99,7 @@
libhidlbase \
libhidltransport \
libhwbinder \
+ android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
libmemunreachable
@@ -165,9 +172,11 @@
LOCAL_SRC_FILES := \
$(statsd_common_src) \
+ tests/dimension_test.cpp \
tests/AnomalyMonitor_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
+ tests/external/puller_util_test.cpp \
tests/indexed_priority_queue_test.cpp \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
@@ -190,7 +199,8 @@
tests/e2e/WakelockDuration_e2e_test.cpp \
tests/e2e/MetricConditionLink_e2e_test.cpp \
tests/e2e/Attribution_e2e_test.cpp \
- tests/e2e/GaugeMetric_e2e_test.cpp
+ tests/e2e/GaugeMetric_e2e_test.cpp \
+ tests/e2e/DimensionInCondition_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 857a6dd..8483b02 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -67,12 +67,16 @@
return hashDimensionsValue(0, value);
}
+android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) {
+ android::hash_t hash = seed;
+ hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey));
+ return JenkinsHashWhiten(hash);
+}
+
using std::string;
string HashableDimensionKey::toString() const {
- string flattened;
- DimensionsValueToString(getDimensionsValue(), &flattened);
- return flattened;
+ return DimensionsValueToString(getDimensionsValue());
}
bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
@@ -162,6 +166,24 @@
return LessThan(getDimensionsValue(), that.getDimensionsValue());
};
+string MetricDimensionKey::toString() const {
+ string flattened = mDimensionKeyInWhat.toString();
+ flattened += mDimensionKeyInCondition.toString();
+ return flattened;
+}
+
+bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
+ return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
+ mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+};
+
+bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
+ return toString().compare(that.toString()) < 0;
+};
+
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+ return EqualsTo(s1, s2);
+}
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 85c317f..a31d7a6 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -41,6 +41,10 @@
return mDimensionsValue;
}
+ inline DimensionsValue* getMutableDimensionsValue() {
+ return &mDimensionsValue;
+ }
+
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
@@ -53,8 +57,52 @@
DimensionsValue mDimensionsValue;
};
+class MetricDimensionKey {
+ public:
+ explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
+ const HashableDimensionKey& dimensionKeyInCondition)
+ : mDimensionKeyInWhat(dimensionKeyInWhat),
+ mDimensionKeyInCondition(dimensionKeyInCondition) {};
+
+ MetricDimensionKey(){};
+
+ MetricDimensionKey(const MetricDimensionKey& that)
+ : mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
+ mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+
+ MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
+
+ std::string toString() const;
+
+ inline const HashableDimensionKey& getDimensionKeyInWhat() const {
+ return mDimensionKeyInWhat;
+ }
+
+ inline const HashableDimensionKey& getDimensionKeyInCondition() const {
+ return mDimensionKeyInCondition;
+ }
+
+ bool hasDimensionKeyInCondition() const {
+ return mDimensionKeyInCondition.getDimensionsValue().has_field();
+ }
+
+ bool operator==(const MetricDimensionKey& that) const;
+
+ bool operator<(const MetricDimensionKey& that) const;
+
+ inline const char* c_str() const {
+ return toString().c_str();
+ }
+ private:
+ HashableDimensionKey mDimensionKeyInWhat;
+ HashableDimensionKey mDimensionKeyInCondition;
+};
+
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2);
+
android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
android::hash_t hashDimensionsValue(const DimensionsValue& value);
+android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey);
} // namespace statsd
} // namespace os
@@ -63,6 +111,7 @@
namespace std {
using android::os::statsd::HashableDimensionKey;
+using android::os::statsd::MetricDimensionKey;
template <>
struct hash<HashableDimensionKey> {
@@ -71,4 +120,14 @@
}
};
-} // namespace std
+template <>
+struct hash<MetricDimensionKey> {
+ std::size_t operator()(const MetricDimensionKey& key) const {
+ android::hash_t hash = hashDimensionsValue(
+ key.getDimensionKeyInWhat().getDimensionsValue());
+ hash = android::JenkinsHashMix(hash,
+ hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue()));
+ return android::JenkinsHashWhiten(hash);
+ }
+};
+} // namespace std
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a4066aa..7662c40 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "statslog.h"
@@ -32,6 +32,7 @@
#include <log/log_event_list.h>
#include <utils/Errors.h>
+#include <utils/SystemClock.h>
using namespace android;
using android::base::StringPrintf;
@@ -60,6 +61,8 @@
// for ConfigMetricsReport
const int FIELD_ID_METRICS = 1;
const int FIELD_ID_UID_MAP = 2;
+const int FIELD_ID_LAST_REPORT_NANOS = 3;
+const int FIELD_ID_CURRENT_REPORT_NANOS = 4;
#define STATS_DATA_DIR "/data/misc/stats-data"
@@ -146,6 +149,12 @@
return;
}
+ long curTime = time(nullptr);
+ if (curTime - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
+ mStatsPullerManager.ClearPullerCacheIfNecessary(curTime);
+ mLastPullerCacheClearTimeSec = curTime;
+ }
+
if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) {
// Map the isolated uid to host uid if necessary.
mapIsolatedUidToHostUidIfNecessaryLocked(event);
@@ -160,7 +169,7 @@
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGD("Updated configuration for key %s", key.ToString().c_str());
+ VLOG("Updated configuration for key %s", key.ToString().c_str());
sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
@@ -258,6 +267,12 @@
uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
+ // Fill in the timestamps.
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_NANOS,
+ (long long)it->second->getLastReportTimeNs());
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_NANOS,
+ (long long)::android::elapsedRealtimeNano());
+
// End of ConfigMetricsReport (reports).
proto.end(reportsToken);
@@ -290,6 +305,10 @@
StatsdStats::getInstance().noteConfigRemoved(key);
mLastBroadcastTimes.erase(key);
+
+ if (mMetricsManagers.empty()) {
+ mStatsPullerManager.ForceClearPullerCache();
+ }
}
void StatsLogProcessor::flushIfNecessaryLocked(
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c19ff63..8bbcd75 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -21,6 +21,7 @@
#include "logd/LogReader.h"
#include "metrics/MetricsManager.h"
#include "packages/UidMap.h"
+#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -75,6 +76,8 @@
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
+ StatsPullerManager mStatsPullerManager;
+
sp<AnomalyMonitor> mAnomalyMonitor;
void onDumpReportLocked(const ConfigKey& key, vector<uint8_t>* outData);
@@ -96,15 +99,21 @@
const long mTimeBaseSec;
+ long mLastPullerCacheClearTimeSec = 0;
+
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
+ FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration);
+ FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration);
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);
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f545bb0..c24efec 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsService.h"
@@ -34,6 +34,7 @@
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
#include <utils/String16.h>
+#include <statslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
@@ -76,18 +77,18 @@
: mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
{
mUidMap = new UidMap();
+ StatsPuller::SetUidMap(mUidMap);
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) {
sp<IStatsCompanionService> sc = getStatsCompanionService();
auto receiver = mConfigManager->GetConfigReceiver(key);
if (sc == nullptr) {
VLOG("Could not find StatsCompanionService");
- } else if (receiver.first.size() == 0) {
+ } else if (receiver == nullptr) {
VLOG("Statscompanion could not find a broadcast receiver for %s",
key.ToString().c_str());
} else {
- sc->sendBroadcast(String16(receiver.first.c_str()),
- String16(receiver.second.c_str()));
+ sc->sendDataBroadcast(receiver);
}
});
@@ -235,6 +236,14 @@
if (!args[0].compare(String8("write-to-disk"))) {
return cmd_write_data_to_disk(out);
}
+
+ if (!args[0].compare(String8("log-app-hook"))) {
+ return cmd_log_app_hook(out, args);
+ }
+
+ if (!args[0].compare(String8("clear-puller-cache"))) {
+ return cmd_clear_puller_cache(out);
+ }
}
print_cmd_help(out);
@@ -272,6 +281,15 @@
fprintf(out, " Flushes all data on memory to disk.\n");
fprintf(out, "\n");
fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats log-app-hook [UID] LABEL STATE\n");
+ fprintf(out, " Writes an AppHook event to the statslog buffer.\n");
+ fprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
+ fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
+ fprintf(out, " uid is used.\n");
+ fprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
+ fprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
fprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
fprintf(out, "\n");
@@ -306,6 +324,10 @@
fprintf(out, "\n");
fprintf(out, "usage: adb shell cmd stats print-stats\n");
fprintf(out, " Prints some basic stats.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
+ fprintf(out, " Clear cached puller data.\n");
}
status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
@@ -344,12 +366,14 @@
}
auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, StrToInt64(name)));
sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc != nullptr) {
- sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+ if (sc == nullptr) {
+ VLOG("Could not access statsCompanion");
+ } else if (receiver == nullptr) {
+ VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str())
+ } else {
+ sc->sendDataBroadcast(receiver);
VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
args[2].c_str());
- } else {
- VLOG("Could not access statsCompanion");
}
return NO_ERROR;
@@ -523,6 +547,42 @@
return NO_ERROR;
}
+status_t StatsService::cmd_log_app_hook(FILE* out, const Vector<String8>& args) {
+ bool good = false;
+ int32_t uid;
+ int32_t label;
+ int32_t state;
+ const int argCount = args.size();
+ if (argCount == 3) {
+ // Automatically pick the UID
+ uid = IPCThreadState::self()->getCallingUid();
+ label = atoi(args[1].c_str());
+ state = atoi(args[2].c_str());
+ good = true;
+ } else if (argCount == 4) {
+ uid = atoi(args[1].c_str());
+ // If it's a userdebug or eng build, then the shell user can impersonate other uids.
+ // Otherwise, the uid must match the actual caller's uid.
+ if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) {
+ label = atoi(args[2].c_str());
+ state = atoi(args[3].c_str());
+ good = true;
+ } else {
+ fprintf(out,
+ "Selecting a UID for writing AppHook can only be dumped for other UIDs on eng"
+ " or userdebug builds.\n");
+ }
+ }
+ if (good) {
+ fprintf(out, "Logging AppHook(%d, %d, %d) to statslog.\n", uid, label, state);
+ android::util::stats_write(android::util::APP_HOOK, uid, label, state);
+ } else {
+ print_cmd_help(out);
+ return UNKNOWN_ERROR;
+ }
+ return NO_ERROR;
+}
+
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
@@ -551,6 +611,18 @@
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());
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ int cleared = mStatsPullerManager.ForceClearPullerCache();
+ fprintf(out, "Puller removed %d cached data!\n", cleared);
+ return NO_ERROR;
+ } else {
+ return PERMISSION_DENIED;
+ }
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& app) {
VLOG("StatsService::informAllUidData was called");
@@ -729,7 +801,6 @@
Status StatsService::addConfiguration(int64_t key,
const vector <uint8_t>& config,
- const String16& package, const String16& cls,
bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
if (checkCallingPermission(String16(kPermissionDump))) {
@@ -740,8 +811,33 @@
return Status::ok();
}
mConfigManager->UpdateConfig(configKey, cfg);
- mConfigManager->SetConfigReceiver(configKey, string(String8(package).string()),
- string(String8(cls).string()));
+ *success = true;
+ return Status::ok();
+ } else {
+ *success = false;
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
+}
+
+Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->RemoveConfigReceiver(configKey);
+ *success = true;
+ return Status::ok();
+ } else {
+ *success = false;
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
+}
+
+Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
+ bool* success) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->SetConfigReceiver(configKey, intentSender);
*success = true;
return Status::ok();
} else {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index be20893..3dc19fe 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -90,9 +90,19 @@
* Binder call to let clients send a configuration and indicate they're interested when they
* should requestData for this configuration.
*/
- virtual Status addConfiguration(int64_t key, const vector <uint8_t>& config,
- const String16& package, const String16& cls, bool* success)
- override;
+ virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config,
+ bool* success) override;
+
+ /**
+ * Binder call to let clients register the data fetch operation for a configuration.
+ */
+ virtual Status setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
+ bool* success) override;
+
+ /**
+ * Binder call to remove the data fetch operation for the specified config key.
+ */
+ virtual Status removeDataFetchOperation(int64_t key, bool* success) override;
/**
* Binder call to allow clients to remove the specified configuration.
@@ -183,6 +193,11 @@
status_t cmd_write_data_to_disk(FILE* out);
/**
+ * Write an AppHook event to the StatsLog buffer, as though StatsLog.write(APP_HOOK).
+ */
+ status_t cmd_log_app_hook(FILE* out, const Vector<String8>& args);
+
+ /**
* Print contents of a pulled metrics source.
*/
status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
@@ -197,6 +212,11 @@
*/
status_t cmd_dump_memory_info(FILE* out);
+ /*
+ * Clear all puller cached data
+ */
+ status_t cmd_clear_puller_cache(FILE* out);
+
/**
* Update a configuration.
*/
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 4912648..72d29d0 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include "anomaly/AnomalyMonitor.h"
@@ -36,10 +36,10 @@
sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
if (statsCompanionService == nullptr) {
- if (DEBUG) ALOGD("Erasing link to statsCompanionService");
+ VLOG("Erasing link to statsCompanionService");
return;
}
- if (DEBUG) ALOGD("Creating link to statsCompanionService");
+ VLOG("Creating link to statsCompanionService");
const sp<const AnomalyAlarm> top = mPq.top();
if (top != nullptr) {
updateRegisteredAlarmTime_l(top->timestampSec);
@@ -58,7 +58,7 @@
return;
}
// TODO: Ensure that refractory period is respected.
- if (DEBUG) ALOGD("Adding alarm with time %u", alarm->timestampSec);
+ VLOG("Adding alarm with time %u", alarm->timestampSec);
mPq.push(alarm);
if (mRegisteredAlarmTimeSec < 1 ||
alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
@@ -72,16 +72,16 @@
ALOGW("Asked to remove a null alarm.");
return;
}
- if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec);
+ VLOG("Removing alarm with time %u", alarm->timestampSec);
bool wasPresent = mPq.remove(alarm);
if (!wasPresent) return;
if (mPq.empty()) {
- if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
+ VLOG("Queue is empty. Cancel any alarm.");
cancelRegisteredAlarmTime_l();
return;
}
uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
- if (DEBUG) ALOGD("Soonest alarm is %u", soonestAlarmTimeSec);
+ VLOG("Soonest alarm is %u", soonestAlarmTimeSec);
if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
updateRegisteredAlarmTime_l(soonestAlarmTimeSec);
}
@@ -91,7 +91,7 @@
// updates to the registered alarm.
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popSoonerThan(
uint32_t timestampSec) {
- if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec);
+ VLOG("Removing alarms with time <= %u", timestampSec);
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms;
std::lock_guard<std::mutex> lock(mLock);
@@ -103,7 +103,7 @@
// Always update registered alarm time (if anything has changed).
if (!oldAlarms.empty()) {
if (mPq.empty()) {
- if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
+ VLOG("Queue is empty. Cancel any alarm.");
cancelRegisteredAlarmTime_l();
} else {
// Always update the registered alarm in this case (unlike remove()).
@@ -114,7 +114,7 @@
}
void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
- if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec);
+ VLOG("Updating reg alarm time to %u", timestampSec);
mRegisteredAlarmTimeSec = timestampSec;
if (mStatsCompanionService != nullptr) {
mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
@@ -123,7 +123,7 @@
}
void AnomalyMonitor::cancelRegisteredAlarmTime_l() {
- if (DEBUG) ALOGD("Cancelling reg alarm.");
+ VLOG("Cancelling reg alarm.");
mRegisteredAlarmTimeSec = 0;
if (mStatsCompanionService != nullptr) {
mStatsCompanionService->cancelAnomalyAlarm();
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ded6c4c..63f6e2a 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "AnomalyTracker.h"
#include "external/Perfetto.h"
-#include "guardrail/StatsdStats.h"
#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+#include "guardrail/StatsdStats.h"
+#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
-#include <android/os/IIncidentManager.h>
-#include <android/os/IncidentReportArgs.h>
-#include <binder/IServiceManager.h>
#include <statslog.h>
#include <time.h>
@@ -35,20 +33,17 @@
// TODO: Get rid of bucketNumbers, and return to the original circular array method.
AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
- : mAlert(alert),
- mConfigKey(configKey),
- mNumOfPastBuckets(mAlert.num_buckets() - 1) {
+ : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) {
VLOG("AnomalyTracker() called");
if (mAlert.num_buckets() <= 0) {
- ALOGE("Cannot create AnomalyTracker with %lld buckets",
- (long long)mAlert.num_buckets());
+ ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets());
return;
}
if (!mAlert.has_trigger_if_sum_gt()) {
ALOGE("Cannot create AnomalyTracker without threshold");
return;
}
- resetStorage(); // initialization
+ resetStorage(); // initialization
}
AnomalyTracker::~AnomalyTracker() {
@@ -96,7 +91,7 @@
}
}
-void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
const int64_t& bucketNum) {
flushPastBuckets(bucketNum);
@@ -147,7 +142,7 @@
}
}
-int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
const int64_t& bucketNum) const {
const auto& bucket = mPastBuckets[index(bucketNum)];
if (bucket == nullptr) {
@@ -157,7 +152,7 @@
return itr == bucket->end() ? 0 : itr->second;
}
-int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
const auto& itr = mSumOverPastBuckets.find(key);
if (itr != mSumOverPastBuckets.end()) {
return itr->second;
@@ -165,17 +160,17 @@
return 0;
}
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (currentBucketNum > mMostRecentBucketNum + 1) {
// TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this.
addPastBucket(key, 0, currentBucketNum - 1);
}
- return mAlert.has_trigger_if_sum_gt()
- && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
+ return mAlert.has_trigger_if_sum_gt() &&
+ getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
}
-void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) {
// TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
if (isInRefractoryPeriod(timestampNs, key)) {
VLOG("Skipping anomaly declaration since within refractory period");
@@ -188,7 +183,7 @@
if (!mSubscriptions.empty()) {
if (mAlert.has_id()) {
- ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id());
+ ALOGI("An anomaly (%llu) has occurred! Informing subscribers.", mAlert.id());
informSubscribers(key);
} else {
ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers.");
@@ -199,14 +194,14 @@
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
- // TODO: This should also take in the const HashableDimensionKey& key?
+ // TODO: This should also take in the const MetricDimensionKey& key?
android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
mConfigKey.GetId(), mAlert.id());
}
void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
const int64_t& currBucketNum,
- const HashableDimensionKey& key,
+ const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
declareAnomaly(timestampNs, key);
@@ -214,7 +209,7 @@
}
bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
- const HashableDimensionKey& key) {
+ const MetricDimensionKey& key) {
const auto& it = mRefractoryPeriodEndsSec.find(key);
if (it != mRefractoryPeriodEndsSec.end()) {
if ((timestampNs / NS_PER_SEC) <= it->second) {
@@ -226,51 +221,33 @@
return false;
}
-void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
VLOG("informSubscribers called.");
if (mSubscriptions.empty()) {
ALOGE("Attempt to call with no subscribers.");
return;
}
- std::set<int> incidentdSections;
-
for (const Subscription& subscription : mSubscriptions) {
switch (subscription.subscriber_information_case()) {
case Subscription::SubscriberInformationCase::kIncidentdDetails:
- for (int i = 0; i < subscription.incidentd_details().section_size(); i++) {
- incidentdSections.insert(subscription.incidentd_details().section(i));
+ if (!GenerateIncidentReport(subscription.incidentd_details(), mAlert, mConfigKey)) {
+ ALOGW("Failed to generate incident report.");
}
break;
case Subscription::SubscriberInformationCase::kPerfettoDetails:
- CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
+ if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) {
+ ALOGW("Failed to generate prefetto traces.");
+ }
break;
case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
- SubscriberReporter::getInstance()
- .alertBroadcastSubscriber(mConfigKey, subscription, key);
+ SubscriberReporter::getInstance().alertBroadcastSubscriber(mConfigKey, subscription,
+ key);
break;
default:
break;
}
}
- if (!incidentdSections.empty()) {
- sp<IIncidentManager> service = interface_cast<IIncidentManager>(
- defaultServiceManager()->getService(android::String16("incident")));
- if (service != NULL) {
- IncidentReportArgs incidentReport;
- for (const auto section : incidentdSections) {
- incidentReport.addSection(section);
- }
- android::os::IncidentHeaderProto header;
- header.set_alert_id(mAlert.id());
- header.mutable_config_key()->set_uid(mConfigKey.GetUid());
- header.mutable_config_key()->set_id(mConfigKey.GetId());
- incidentReport.addHeader(header);
- service->reportIncident(incidentReport);
- } else {
- ALOGW("Couldn't get the incident service.");
- }
- }
}
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 472c02c..3be959d 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -16,22 +16,24 @@
#pragma once
+#include <memory> // unique_ptr
+
+#include <stdlib.h>
+
#include <gtest/gtest_prod.h>
+#include <utils/RefBase.h>
+
#include "AnomalyMonitor.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
#include "stats_util.h" // HashableDimensionKey and DimToValMap
-#include <memory> // unique_ptr
-#include <stdlib.h>
-#include <utils/RefBase.h>
-
namespace android {
namespace os {
namespace statsd {
-using std::unordered_map;
using std::shared_ptr;
+using std::unordered_map;
// Does NOT allow negative values.
class AnomalyTracker : public virtual RefBase {
@@ -48,31 +50,30 @@
// Adds a bucket.
// Bucket index starts from 0.
void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
- void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+ void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
const int64_t& bucketNum);
// Returns true if detected anomaly for the existing buckets on one or more dimension keys.
- bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+ bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Informs incidentd about the detected alert.
- void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key);
+ void declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key);
// Detects the alert and informs the incidentd when applicable.
void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
- const HashableDimensionKey& key,
- const int64_t& currentBucketValue);
+ const MetricDimensionKey& key, const int64_t& currentBucketValue);
// Init the AnomalyMonitor which is shared across anomaly trackers.
virtual void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
- return; // Base AnomalyTracker class has no need for the AnomalyMonitor.
+ return; // Base AnomalyTracker class has no need for the AnomalyMonitor.
}
// Helper function to return the sum value of past buckets at given dimension.
- int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+ int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
// Helper function to return the value for a past bucket.
- int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+ int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
// Returns the anomaly threshold.
inline int64_t getAnomalyThreshold() const {
@@ -81,7 +82,7 @@
// Returns the refractory period timestamp (in seconds) for the given key.
// If there is no stored refractory period ending timestamp, returns 0.
- uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const {
+ uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
}
@@ -92,9 +93,10 @@
// Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
// and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor.
- virtual void informAlarmsFired(const uint64_t& timestampNs,
+ virtual void informAlarmsFired(
+ const uint64_t& timestampNs,
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) {
- return; // The base AnomalyTracker class doesn't have alarms.
+ return; // The base AnomalyTracker class doesn't have alarms.
}
protected:
@@ -124,7 +126,7 @@
// declared for that dimension) ends, in seconds. Only anomalies that occur after this period
// ends will be declared.
// Entries may be, but are not guaranteed to be, removed after the period is finished.
- unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
+ unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
void flushPastBuckets(const int64_t& currBucketNum);
@@ -135,7 +137,7 @@
// and remove any items with value 0.
void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
- bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key);
+ bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
// Calculates the corresponding bucket index within the circular array.
size_t index(int64_t bucketNum) const;
@@ -144,7 +146,7 @@
virtual void resetStorage();
// Informs the subscribers that an anomaly has occurred.
- void informSubscribers(const HashableDimensionKey& key);
+ void informSubscribers(const MetricDimensionKey& key);
FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 7576a38..fa0bc0c 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "DurationAnomalyTracker.h"
@@ -37,8 +37,8 @@
if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!");
}
-void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
- const uint64_t& timestampNs) {
+void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
+ const uint64_t& timestampNs) {
auto itr = mAlarms.find(dimensionKey);
if (itr == mAlarms.end()) {
return;
@@ -51,9 +51,8 @@
}
}
-void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
- const uint64_t& timestampNs) {
-
+void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
+ const uint64_t& timestampNs) {
uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period");
@@ -66,7 +65,7 @@
}
}
-void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) {
auto itr = mAlarms.find(dimensionKey);
if (itr != mAlarms.end()) {
mAlarms.erase(dimensionKey);
@@ -77,7 +76,7 @@
}
void DurationAnomalyTracker::stopAllAlarms() {
- std::set<HashableDimensionKey> keys;
+ std::set<MetricDimensionKey> keys;
for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
keys.insert(itr->first);
}
@@ -86,16 +85,16 @@
}
}
-void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs,
+void DurationAnomalyTracker::informAlarmsFired(
+ const uint64_t& timestampNs,
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) {
-
if (firedAlarms.empty() || mAlarms.empty()) return;
// Find the intersection of firedAlarms and mAlarms.
// The for loop is inefficient, since it loops over all keys, but that's okay since it is very
// seldomly called. The alternative would be having AnomalyAlarms store information about the
- // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is
- // rarely ever called.
- unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+ // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that
+ // is rarely ever called.
+ unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> 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 33e55ab..ba687da 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -32,10 +32,10 @@
virtual ~DurationAnomalyTracker();
// Starts the alarm at the given timestamp.
- void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+ void startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& eventTime);
// Stops the alarm.
- void stopAlarm(const HashableDimensionKey& dimensionKey);
+ void stopAlarm(const MetricDimensionKey& dimensionKey);
// Stop all the alarms owned by this tracker.
void stopAllAlarms();
@@ -46,20 +46,21 @@
}
// Declares the anomaly when the alarm expired given the current timestamp.
- void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs);
// Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
// and removes it from firedAlarms.
// Note that this will generally be called from a different thread from the other functions;
// the caller is responsible for thread safety.
- void informAlarmsFired(const uint64_t& timestampNs,
+ void informAlarmsFired(
+ const uint64_t& timestampNs,
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& 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<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+ std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
// Anomaly alarm monitor.
sp<AnomalyMonitor> mAnomalyMonitor;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4e570a6..6bca16f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -95,11 +95,14 @@
AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
ShutdownSequenceReported shutdown_sequence_reported = 56;
BootSequenceReported boot_sequence_reported = 57;
+ DaveyOccurred davey_occurred = 58;
+ OverlayStateChanged overlay_state_changed = 59;
+ ForegroundServiceStateChanged foreground_service_state_changed = 60;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
// Pulled events will start at field 10000.
- // Next: 10019
+ // Next: 10021
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -120,6 +123,8 @@
CpuActiveTime cpu_active_time = 10016;
CpuClusterTime cpu_cluster_time = 10017;
DiskSpace disk_space = 10018;
+ RemainingBatteryCapacity remaining_battery_capacity = 10019;
+ FullBatteryCapacity full_battery_capacity = 10020;
}
}
@@ -721,6 +726,17 @@
}
/**
+ * Logs the duration of a davey (jank of >=700ms) when it occurs
+ *
+ * Logged from:
+ * frameworks/base/libs/hwui/JankTracker.cpp
+ */
+message DaveyOccurred {
+ // Amount of time it took to render the frame. Should be >=700ms.
+ optional int64 jank_duration_ms = 1;
+}
+
+/**
* Logs phone signal strength changes.
*
* Logged from:
@@ -757,8 +773,8 @@
// The tag used with the is_default for resetting sets of settings. This is generally null.
optional string tag = 5;
- // 1 indicates that this setting with tag should be resettable.
- optional int32 is_default = 6;
+ // True if this setting with tag should be resettable.
+ optional bool is_default = 6;
// The user ID associated. Defined in android/os/UserHandle.java
optional int32 user = 7;
@@ -965,6 +981,176 @@
}
/**
+ * Logs overlay action
+ * Logged from:
+ * services/core/java/com/android/server/wm/Session.java
+ */
+message OverlayStateChanged {
+ optional int32 uid = 1;
+
+ optional string package_name = 2;
+
+ optional bool using_alert_window = 3;
+
+ enum State {
+ ENTERED = 1;
+ EXITED = 2;
+ }
+ optional State state = 4;
+}
+
+/*
+ * Logs foreground service starts and stops.
+ * Note that this is not when a service starts or stops, but when it is
+ * considered foreground.
+ * Logged from
+ * //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ForegroundServiceStateChanged {
+ optional int32 uid = 1;
+ // package_name + "/" + class_name
+ optional string short_name = 2;
+
+ enum State {
+ ENTER = 1;
+ EXIT = 2;
+ }
+ optional State state = 3;
+}
+
+/**
+ * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky
+ * behavior in its own uid. However, the metrics of these isolated uid's almost always should be
+ * attributed back to the parent (host) uid. One example is Chrome.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message IsolatedUidChanged {
+ // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
+ optional int32 parent_uid = 1;
+
+ optional int32 isolated_uid = 2;
+
+ // We expect an isolated uid to be removed before if it's used for another parent uid.
+ enum Event {
+ REMOVED = 0;
+ CREATED = 1;
+ }
+ optional Event event = 3;
+}
+
+/*
+ * Logs the reception of an incoming network packet causing the main system to wake up for
+ * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd
+ * and processed by WakeupController.cpp.
+ */
+message PacketWakeupOccurred {
+ // The uid owning the socket into which the packet was delivered, or -1 if the packet was
+ // delivered nowhere.
+ optional int32 uid = 1;
+ // The interface name on which the packet was received.
+ optional string iface = 2;
+ // The ethertype value of the packet.
+ optional int32 ethertype = 3;
+ // String representation of the destination MAC address of the packet.
+ optional string destination_hardware_address = 4;
+ // String representation of the source address of the packet if this was an IP packet.
+ optional string source_ip = 5;
+ // String representation of the destination address of the packet if this was an IP packet.
+ optional string destination_ip = 6;
+ // The value of the protocol field if this was an IPv4 packet or the value of the Next Header
+ // field if this was an IPv6 packet. The range of possible values is the same for both IP
+ // families.
+ optional int32 ip_next_header = 7;
+ // The source port if this was a TCP or UDP packet.
+ optional int32 source_port = 8;
+ // The destination port if this was a TCP or UDP packet.
+ optional int32 destination_port = 9;
+}
+
+/*
+ * Logs the memory stats for an app on startup.
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppStartMemoryStateCaptured {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1;
+
+ // The process name.
+ optional string process_name = 2;
+
+ // The activity name.
+ optional string activity_name = 3;
+
+ // # of page-faults
+ optional int64 pgfault = 4;
+
+ // # of major page-faults
+ optional int64 pgmajfault = 5;
+
+ // RSS
+ optional int64 rss_in_bytes = 6;
+
+ // CACHE
+ optional int64 cache_in_bytes = 7;
+
+ // SWAP
+ optional int64 swap_in_bytes = 8;
+}
+
+/*
+ * Logs the change in Low Memory Killer Daemon (LMKD) state which is used as start/stop boundaries
+ * for LMK event.
+ * Logged from:
+ * system/core/lmkd/lmkd.c
+ */
+message LmkStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ START = 1;
+ STOP = 2;
+ }
+ optional State state = 1;
+}
+
+/*
+ * Logs the event when Low Memory Killer Daemon (LMKD) kills a process to reduce memory pressure.
+ * Logged from:
+ * system/core/lmkd/lmkd.c
+ */
+message LmkKillOccurred {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1;
+
+ // The process name.
+ optional string process_name = 2;
+
+ // oom adj score.
+ optional int32 oom_score = 3;
+
+ // # of page-faults
+ optional int64 pgfault = 4;
+
+ // # of major page-faults
+ optional int64 pgmajfault = 5;
+
+ // RSS
+ optional int64 rss_in_bytes = 6;
+
+ // CACHE
+ optional int64 cache_in_bytes = 7;
+
+ // SWAP
+ optional int64 swap_in_bytes = 8;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Pulled atoms below this line //
+//////////////////////////////////////////////////////////////////////
+
+/**
* Pulls bytes transferred via wifi (Sum of foreground and background usage).
*
* Pulled from:
@@ -1080,34 +1266,15 @@
* hardware/interfaces/power/1.1/types.hal
*/
message SubsystemSleepState {
- // Name should be in the format of XXX.YYY where XXX is subsystem name,
- // YYY is corresponding voter name.
- // If there are no voters, the format should just be XXX (with no dot).
- // XXX and YYY should not contain a "." in it.
- optional string name = 1;
+ // Subsystem name
+ optional string subsystem_name = 1;
+ // For PlatformLowPowerStats (hal 1.0), this is the voter name, which could be empty.
+ // For SubsystemLowPowerStats (hal 1.1), this is the sleep state name.
+ optional string subname = 2;
// The number of times it entered, or voted for entering the sleep state
- optional uint64 count = 2;
+ optional uint64 count = 3;
// The length of time spent in, or spent voting for, the sleep state
- optional uint64 timeMs = 3;
-}
-
-/**
- * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky
- * behavior in its own uid. However, the metrics of these isolated uid's almost always should be
- * attributed back to the parent (host) uid. One example is Chrome.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message IsolatedUidChanged {
- // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
- optional int32 parent_uid = 1;
-
- optional int32 isolated_uid = 2;
-
- // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
- // be removed before if it's used for another parent uid.
- optional int32 is_create = 3;
+ optional uint64 timeMs = 4;
}
/**
@@ -1144,35 +1311,6 @@
optional uint64 time_ms = 3;
}
-/*
- * Logs the reception of an incoming network packet causing the main system to wake up for
- * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd
- * and processed by WakeupController.cpp.
- */
-message PacketWakeupOccurred {
- // The uid owning the socket into which the packet was delivered, or -1 if the packet was
- // delivered nowhere.
- optional int32 uid = 1;
- // The interface name on which the packet was received.
- optional string iface = 2;
- // The ethertype value of the packet.
- optional int32 ethertype = 3;
- // String representation of the destination MAC address of the packet.
- optional string destination_hardware_address = 4;
- // String representation of the source address of the packet if this was an IP packet.
- optional string source_ip = 5;
- // String representation of the destination address of the packet if this was an IP packet.
- optional string destination_ip = 6;
- // The value of the protocol field if this was an IPv4 packet or the value of the Next Header
- // field if this was an IPv6 packet. The range of possible values is the same for both IP
- // families.
- optional int32 ip_next_header = 7;
- // The source port if this was a TCP or UDP packet.
- optional int32 source_port = 8;
- // The destination port if this was a TCP or UDP packet.
- optional int32 destination_port = 9;
-}
-
/**
* Pulls Wifi Controller Activity Energy Info
*/
@@ -1246,37 +1384,6 @@
}
/*
- * Logs the memory stats for an app on startup.
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message AppStartMemoryStateCaptured {
- // The uid if available. -1 means not available.
- optional int32 uid = 1;
-
- // The process name.
- optional string process_name = 2;
-
- // The activity name.
- optional string activity_name = 3;
-
- // # of page-faults
- optional int64 pgfault = 4;
-
- // # of major page-faults
- optional int64 pgmajfault = 5;
-
- // RSS
- optional int64 rss_in_bytes = 6;
-
- // CACHE
- optional int64 cache_in_bytes = 7;
-
- // SWAP
- optional int64 swap_in_bytes = 8;
-}
-
-/*
* Logs the memory stats for a process.
*/
message ProcessMemoryState {
@@ -1306,52 +1413,6 @@
}
/*
- * Logs the change in Low Memory Killer Daemon (LMKD) state which is used as start/stop boundaries
- * for LMK event.
- * Logged from:
- * system/core/lmkd/lmkd.c
- */
-message LmkStateChanged {
- enum State {
- UNKNOWN = 0;
- START = 1;
- STOP = 2;
- }
- optional State state = 1;
-}
-
-/*
- * Logs the event when Low Memory Killer Daemon (LMKD) kills a process to reduce memory pressure.
- * Logged from:
- * system/core/lmkd/lmkd.c
- */
-message LmkKillOccurred {
- // The uid if available. -1 means not available.
- optional int32 uid = 1;
-
- // The process name.
- optional string process_name = 2;
-
- // oom adj score.
- optional int32 oom_score = 3;
-
- // # of page-faults
- optional int64 pgfault = 4;
-
- // # of major page-faults
- optional int64 pgmajfault = 5;
-
- // RSS
- optional int64 rss_in_bytes = 6;
-
- // CACHE
- optional int64 cache_in_bytes = 7;
-
- // SWAP
- optional int64 swap_in_bytes = 8;
-}
-
-/*
* Elapsed real time from SystemClock.
*/
message SystemElapsedRealtime {
@@ -1410,3 +1471,20 @@
// available bytes in download cache or temp directories
optional uint64 temp_available_bytes = 3;
}
+
+/**
+ * Pulls battery coulomb counter, which is the remaining battery charge in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message RemainingBatteryCapacity {
+ optional int32 charge_uAh = 1;
+}
+
+/**
+ * Pulls battery capacity, which is the battery capacity when full in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message FullBatteryCapacity {
+ optional int32 capacity_uAh = 1;
+}
+
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index ea6586c..4c20ccb 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -78,6 +78,7 @@
return false;
}
+
bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
conditionIdIndexMap, stack);
@@ -88,8 +89,10 @@
ALOGW("Child initialization success %lld ", (long long)child);
}
+ if (allConditionTrackers[childIndex]->isSliced()) {
+ setSliced(true);
+ }
mChildren.push_back(childIndex);
-
mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
childTracker->getLogTrackerIndex().end());
}
@@ -105,11 +108,15 @@
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
- vector<ConditionState>& conditionCache) const {
+ const FieldMatcher& dimensionFields,
+ vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &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,
- conditionCache);
+ dimensionFields, conditionCache,
+ dimensionsKeySet);
}
}
conditionCache[mIndex] =
@@ -127,6 +134,7 @@
}
for (const int childIndex : mChildren) {
+ // So far, this is fine as there is at most one child having sliced output.
if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
const sp<ConditionTracker>& child = mAllConditions[childIndex];
child->evaluateCondition(event, eventMatcherValues, mAllConditions,
@@ -159,6 +167,24 @@
}
}
+ConditionState CombinationConditionTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ vector<ConditionState> 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);
+ }
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+ if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
+ dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
+ }
+ return conditionCache[mIndex];
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dfd3837..0b7f949 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,12 +41,20 @@
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const override;
+ void isConditionMet(
+ const ConditionKey& conditionParameters,
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
private:
LogicalOperation mLogicalOperation;
+
// Store index of the children Predicates.
// We don't store string name of the Children, because we want to get rid of the hash map to
// map the name to object. We don't want to store smart pointers to children, because it
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 773860f..81abbdb 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -24,6 +24,7 @@
#include <log/logprint.h>
#include <utils/RefBase.h>
+#include <unordered_set>
#include <unordered_map>
namespace android {
@@ -84,10 +85,19 @@
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
// [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<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const = 0;
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+
+ virtual ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
virtual const std::set<int>& getLogTrackerIndex() const {
@@ -98,6 +108,10 @@
mSliced = mSliced | sliced;
}
+ bool isSliced() const {
+ return mSliced;
+ }
+
protected:
const int64_t mConditionId;
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index d99c2cc..0427700 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "ConditionWizard.h"
+#include <unordered_set>
namespace android {
namespace os {
@@ -23,14 +24,26 @@
using std::string;
using std::vector;
-ConditionState ConditionWizard::query(const int index,
- const ConditionKey& parameters) {
+ConditionState ConditionWizard::query(
+ const int index, const ConditionKey& parameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet) {
+
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
- mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
+ mAllConditions[index]->isConditionMet(
+ parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet);
return cache[index];
}
+ConditionState ConditionWizard::getMetConditionDimension(
+ const int index, const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const {
+
+ return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
+ *dimensionsKeySet);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4ff5c07..b38b59f 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -41,7 +41,14 @@
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(
const int conditionIndex,
- const ConditionKey& conditionParameters);
+ const ConditionKey& conditionParameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet);
+
+ virtual ConditionState getMetConditionDimension(
+ const int index,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const;
private:
std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 5cfc349..25265d5 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -104,6 +104,9 @@
vector<bool>& stack) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
+ if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) {
+ setSliced(true);
+ }
return mInitialized;
}
@@ -234,11 +237,12 @@
conditionChangedCache[mIndex] == true);
}
-void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
+void SimpleConditionTracker::evaluateCondition(
+ const LogEvent& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %lld %d",
@@ -271,7 +275,7 @@
if (mSliced) {
// if the condition result is sliced. metrics won't directly get value from the
// cache, so just set any value other than kNotEvaluated.
- conditionCache[mIndex] = ConditionState::kUnknown;
+ conditionCache[mIndex] = mInitialValue;
} else {
const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
if (itr == mSlicedConditionState.end()) {
@@ -310,10 +314,8 @@
vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
ConditionState::kNotEvaluated);
vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
-
handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
dimensionalConditionCache, dimensionalConditionChangedCache);
-
OrConditionState(dimensionalConditionCache, &conditionCache);
OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
}
@@ -323,42 +325,73 @@
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
- vector<ConditionState>& conditionCache) const {
- const auto pair = conditionParameters.find(mConditionId);
-
- if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
- ALOGE("Predicate %lld output has dimension, but it's not specified in the query!",
- (long long)mConditionId);
- conditionCache[mIndex] = mInitialValue;
+ const FieldMatcher& dimensionFields,
+ vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %lld %d",
+ (long long)mConditionId, conditionCache[mIndex]);
return;
}
- std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+ const auto pair = conditionParameters.find(mConditionId);
+
+ if (pair == conditionParameters.end()) {
+ ConditionState conditionState = ConditionState::kNotEvaluated;
+ if (dimensionFields.has_field() && dimensionFields.child_size() > 0 &&
+ dimensionFields.field() == mOutputDimensions.field()) {
+ conditionState = conditionState | getMetConditionDimension(
+ allConditions, dimensionFields, dimensionsKeySet);
+ } else {
+ conditionState = conditionState | mInitialValue;
+ if (!mSliced) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ }
+ }
+ }
+ conditionCache[mIndex] = conditionState;
+ return;
+ }
+ std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY };
const std::vector<HashableDimensionKey> &keys =
(pair == conditionParameters.end()) ? defaultKeys : pair->second;
ConditionState conditionState = ConditionState::kNotEvaluated;
- for (const auto& key : keys) {
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const HashableDimensionKey& key = keys[i];
auto startedCountIt = mSlicedConditionState.find(key);
if (startedCountIt != mSlicedConditionState.end()) {
- conditionState = conditionState |
- (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+ ConditionState sliceState =
+ startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+ HashableDimensionKey dimensionKey;
+ if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields,
+ dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
} else {
// For unseen key, check whether the require dimensions are subset of sliced condition
// output.
- bool seenDimension = false;
+ conditionState = conditionState | mInitialValue;
for (const auto& slice : mSlicedConditionState) {
- if (IsSubDimension(slice.first.getDimensionsValue(),
- key.getDimensionsValue())) {
- seenDimension = true;
- conditionState = conditionState |
- (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+ ConditionState sliceState =
+ slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) {
+ conditionState = conditionState | sliceState;
+ if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+ HashableDimensionKey dimensionKey;
+ if (getSubDimension(slice.first.getDimensionsValue(),
+ dimensionFields, dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
}
- if (conditionState == ConditionState::kTrue) {
- break;
- }
- }
- if (!seenDimension) {
- conditionState = conditionState | mInitialValue;
}
}
}
@@ -366,6 +399,38 @@
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
+ConditionState SimpleConditionTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ ConditionState conditionState = mInitialValue;
+ if (!dimensionFields.has_field() ||
+ !mOutputDimensions.has_field() ||
+ dimensionFields.field() != mOutputDimensions.field()) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ }
+ return conditionState;
+ }
+
+ for (const auto& slice : mSlicedConditionState) {
+ ConditionState sliceState =
+ slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ DimensionsValue dimensionsValue;
+ conditionState = conditionState | sliceState;
+ HashableDimensionKey dimensionKey;
+ if (sliceState == ConditionState::kTrue &&
+ getSubDimension(slice.first.getDimensionsValue(), dimensionFields,
+ dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
+ return conditionState;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 815b445..ce9a02d 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -48,7 +48,14 @@
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const override;
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
private:
const ConfigKey mConfigKey;
@@ -73,7 +80,8 @@
void handleStopAll(std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
- void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+ void handleConditionEvent(const HashableDimensionKey& outputKey,
+ bool matchStart,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 3b2d480..0ab33cf 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -118,6 +118,9 @@
void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
std::vector<Field> *allFields) {
+ if (matcher.has_position()) {
+ leafField->set_position_index(0);
+ }
if (matcher.child_size() == 0) {
allFields->push_back(*rootField);
return;
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 61eeee3..06ff603 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -75,8 +75,8 @@
}
}
-void ConfigManager::SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls) {
- mConfigReceivers[key] = pair<string, string>(pkg, cls);
+void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+ mConfigReceivers[key] = intentSender;
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -159,10 +159,10 @@
return ret;
}
-const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
auto it = mConfigReceivers.find(key);
if (it == mConfigReceivers.end()) {
- return pair<string,string>();
+ return nullptr;
} else {
return it->second;
}
@@ -175,8 +175,7 @@
fprintf(out, " %6d %lld\n", key.GetUid(), (long long)key.GetId());
auto receiverIt = mConfigReceivers.find(key);
if (receiverIt != mConfigReceivers.end()) {
- fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(),
- receiverIt->second.second.c_str());
+ fprintf(out, " -> received by PendingIntent as binder\n");
}
}
}
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index ad666bc..a2b2a0c 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -16,6 +16,7 @@
#pragma once
+#include "binder/IBinder.h"
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
@@ -68,12 +69,12 @@
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const std::string& pkg, const std::string& cls);
+ void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const std::pair<std::string, std::string> GetConfigReceiver(const ConfigKey& key) const;
+ const sp<android::IBinder> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
@@ -124,10 +125,10 @@
std::set<ConfigKey> mConfigs;
/**
- * Each config key can be subscribed by up to one receiver, specified as the package name and
- * class name.
+ * Each config key can be subscribed by up to one receiver, specified as IBinder from
+ * PendingIntent.
*/
- std::map<ConfigKey, std::pair<std::string, std::string>> mConfigReceivers;
+ std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers;
/**
* The ConfigListeners that will be told about changes.
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
index 04445ca..8a2e871 100644
--- a/cmds/statsd/src/dimension.cpp
+++ b/cmds/statsd/src/dimension.cpp
@@ -253,6 +253,9 @@
}
void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+ if (!value.has_field()) {
+ return;
+ }
*flattened += std::to_string(value.field());
*flattened += ":";
switch (value.value_case()) {
@@ -352,6 +355,46 @@
}
}
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+ DimensionsValue* subDimension) {
+ if (!matcher.has_field()) {
+ return false;
+ }
+ if (matcher.field() != dimension.field()) {
+ return false;
+ }
+ if (matcher.child_size() <= 0) {
+ if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple ||
+ dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) {
+ return false;
+ }
+ *subDimension = dimension;
+ return true;
+ } else {
+ if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) {
+ return false;
+ }
+ bool found_value = true;
+ auto value_tuple = dimension.value_tuple();
+ subDimension->set_field(dimension.field());
+ for (int i = 0; found_value && i < matcher.child_size(); ++i) {
+ int j = 0;
+ for (; j < value_tuple.dimensions_value_size(); ++j) {
+ if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) {
+ break;
+ }
+ }
+ if (j < value_tuple.dimensions_value_size()) {
+ found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i),
+ subDimension->mutable_value_tuple()->add_dimensions_value());
+ } else {
+ found_value = false;
+ }
+ }
+ return found_value;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index e900c5e..138c6e9 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -63,6 +63,9 @@
// Helper function to get long value from the DimensionsValue proto.
long getLongFromDimenValue(const DimensionsValue& dimensionValue);
+
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+ DimensionsValue* subDimension);
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index a751273..d0d2f93 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
index e7ea4b9..d9aeb46 100644
--- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
index 7a2d199..0e126e7 100644
--- a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
index 7426e74..7684ed4 100644
--- a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 1d8a968..b09d373 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
@@ -36,7 +37,7 @@
namespace statsd {
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
- ALOGD("Starting trace collection through perfetto");
+ VLOG("Starting trace collection through perfetto");
if (!config.has_trace_config()) {
ALOGE("The perfetto trace config is empty, aborting");
@@ -118,7 +119,7 @@
return false;
}
- ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded");
+ VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded");
return true;
}
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
new file mode 100644
index 0000000..72fb5ff
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include "external/ResourceHealthManagerPuller.h"
+#include "external/StatsPuller.h"
+
+#include "ResourceHealthManagerPuller.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
+
+bool getHealthHal() {
+ if (gHealthHal == nullptr) {
+ gHealthHal = get_health_service();
+
+ }
+ return gHealthHal != nullptr;
+}
+
+ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
+}
+
+// TODO: add other health atoms (eg. Temperature).
+bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ if (!getHealthHal()) {
+ ALOGE("Health Hal not loaded");
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+
+ data->clear();
+ bool result_success = true;
+
+ Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
+ if (r != Result::SUCCESS) {
+ result_success = false;
+ return;
+ }
+ if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryChargeCounter);
+ ptr->init();
+ data->push_back(ptr);
+ } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryFullCharge);
+ ptr->init();
+ data->push_back(ptr);
+ } else {
+ ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
+ }
+ });
+ if (!result_success || !ret.isOk()) {
+ ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
+ ret.description().c_str());
+ if (!ret.isOk() && ret.isDeadObject()) {
+ gHealthHal = nullptr;
+ }
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
new file mode 100644
index 0000000..9b238eaf5
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads Ihealth.hal
+ */
+class ResourceHealthManagerPuller : public StatsPuller {
+public:
+ ResourceHealthManagerPuller(int tagId);
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index b955f1c..8210c8d 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
@@ -64,7 +64,7 @@
std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
data->push_back(make_shared<LogEvent>(tmp));
}
- ALOGD("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
+ VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
return true;
} else {
ALOGW("statsCompanion not found!");
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index cadc535..fc0ad7c 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -19,6 +19,7 @@
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
+#include "puller_util.h"
namespace android {
namespace os {
@@ -26,6 +27,9 @@
using std::lock_guard;
+sp<UidMap> StatsPuller::mUidMap = nullptr;
+void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
+
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
@@ -54,11 +58,32 @@
mLastPullTimeSec = curTime;
bool ret = PullInternal(&mCachedData);
if (ret) {
- (*data) = mCachedData;
+ mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ (*data) = mCachedData;
}
return ret;
}
+int StatsPuller::ForceClearCache() {
+ return clearCache();
+}
+
+int StatsPuller::clearCache() {
+ lock_guard<std::mutex> lock(mLock);
+ int ret = mCachedData.size();
+ mCachedData.clear();
+ mLastPullTimeSec = 0;
+ return ret;
+}
+
+int StatsPuller::ClearCacheIfNecessary(long timestampSec) {
+ if (timestampSec - mLastPullTimeSec > mCoolDownSec) {
+ return clearCache();
+ } else {
+ return 0;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 47cc9f0..82a8611 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,11 +18,14 @@
#include <android/os/StatsLogEventWrapper.h>
#include <utils/String16.h>
+#include <utils/RefBase.h>
#include <mutex>
#include <vector>
+#include "packages/UidMap.h"
-#include "logd/LogEvent.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "puller_util.h"
using android::os::StatsLogEventWrapper;
@@ -30,7 +33,7 @@
namespace os {
namespace statsd {
-class StatsPuller {
+class StatsPuller : public virtual RefBase {
public:
StatsPuller(const int tagId);
@@ -38,7 +41,15 @@
bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
-protected:
+ // Clear cache immediately
+ int ForceClearCache();
+
+ // Clear cache if elapsed time is more than cooldown time
+ int ClearCacheIfNecessary(long timestampSec);
+
+ static void SetUidMap(const sp<UidMap>& uidMap);
+
+ protected:
// The atom tag id this puller pulls
const int mTagId;
@@ -59,6 +70,10 @@
std::vector<std::shared_ptr<LogEvent>> mCachedData;
long mLastPullTimeSec;
+
+ int clearCache();
+
+ static sp<UidMap> mUidMap;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 00a1475..0dee342 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -50,10 +50,18 @@
return mPullerManager.Pull(tagId, data);
}
- virtual void SetTimeBaseSec(const long timeBaseSec) {
+ void SetTimeBaseSec(const long timeBaseSec) {
mPullerManager.SetTimeBaseSec(timeBaseSec);
}
+ int ForceClearPullerCache() {
+ return mPullerManager.ForceClearPullerCache();
+ }
+
+ int ClearPullerCacheIfNecessary(long timestampSec) {
+ return mPullerManager.ClearPullerCacheIfNecessary(timestampSec);
+ }
+
private:
StatsPullerManagerImpl
& mPullerManager = StatsPullerManagerImpl::GetInstance();
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 148c9ae..4c676a7 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
@@ -23,10 +23,14 @@
#include <climits>
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
+#include "ResourceHealthManagerPuller.h"
+#include "KernelUidCpuActiveTimeReader.h"
+#include "KernelUidCpuClusterTimeReader.h"
#include "SubsystemSleepStatePuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
+#include "SubsystemSleepStatePuller.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -43,59 +47,89 @@
namespace os {
namespace statsd {
+const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
+ // wifi_bytes_transfer
+ {android::util::WIFI_BYTES_TRANSFER,
+ {{2, 3, 4, 5},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+ // wifi_bytes_transfer_by_fg_bg
+ {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
+ {{3, 4, 5, 6},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+ // mobile_bytes_transfer
+ {android::util::MOBILE_BYTES_TRANSFER,
+ {{2, 3, 4, 5},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+ // mobile_bytes_transfer_by_fg_bg
+ {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
+ {{3, 4, 5, 6},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+ // bluetooth_bytes_transfer
+ {android::util::BLUETOOTH_BYTES_TRANSFER,
+ {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+ // kernel_wakelock
+ {android::util::KERNEL_WAKELOCK,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ // subsystem_sleep_state
+ {android::util::SUBSYSTEM_SLEEP_STATE, {{}, {}, 1, new SubsystemSleepStatePuller()}},
+ // cpu_time_per_freq
+ {android::util::CPU_TIME_PER_FREQ,
+ {{3}, {2}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ // cpu_time_per_uid
+ {android::util::CPU_TIME_PER_UID, {{2, 3}, {}, 1, new CpuTimePerUidPuller()}},
+ // cpu_time_per_uid_freq
+ {android::util::CPU_TIME_PER_UID_FREQ, {{3}, {2}, 1, new CpuTimePerUidFreqPuller()}},
+ // wifi_activity_energy_info
+ {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+ // modem_activity_info
+ {android::util::MODEM_ACTIVITY_INFO,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ // bluetooth_activity_info
+ {android::util::BLUETOOTH_ACTIVITY_INFO,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+ // system_elapsed_realtime
+ {android::util::SYSTEM_ELAPSED_REALTIME,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ // system_uptime
+ {android::util::SYSTEM_UPTIME,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ // cpu_active_time
+ {android::util::CPU_ACTIVE_TIME, {{3}, {2}, 1, new KernelUidCpuActiveTimeReader()}},
+ // cpu_cluster_time
+ {android::util::CPU_CLUSTER_TIME, {{3}, {2}, 1, new KernelUidCpuClusterTimeReader()}},
+ // disk_space
+ {android::util::DISK_SPACE,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+ // remaining_battery_capacity
+ {android::util::REMAINING_BATTERY_CAPACITY,
+ {{}, {}, 1, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+ // full_battery_capacity
+ {android::util::FULL_BATTERY_CAPACITY,
+ {{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}};
+
StatsPullerManagerImpl::StatsPullerManagerImpl()
: mCurrentPullingInterval(LONG_MAX) {
- mPullers.insert({android::util::KERNEL_WAKELOCK,
- make_shared<StatsCompanionServicePuller>(android::util::KERNEL_WAKELOCK)});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::WIFI_BYTES_TRANSFER)});
- mPullers.insert(
- {android::util::MOBILE_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::MOBILE_BYTES_TRANSFER)});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- make_shared<StatsCompanionServicePuller>(
- android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- make_shared<StatsCompanionServicePuller>(
- android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert(
- {android::util::SUBSYSTEM_SLEEP_STATE,
- make_shared<SubsystemSleepStatePuller>()});
- mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
- mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
- mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
- mPullers.insert(
- {android::util::SYSTEM_ELAPSED_REALTIME,
- make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_ELAPSED_REALTIME)});
- mPullers.insert({android::util::SYSTEM_UPTIME,
- make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
- mPullers.insert({android::util::DISK_SPACE,
- make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
- mPullers.insert(
- {android::util::BLUETOOTH_ACTIVITY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_ACTIVITY_INFO)});
-
- mPullers.insert(
- {android::util::BLUETOOTH_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_BYTES_TRANSFER)});
- mPullers.insert(
- {android::util::WIFI_ACTIVITY_ENERGY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
- mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
-
mStatsCompanionService = StatsService::getStatsCompanionService();
}
bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
- if (DEBUG) ALOGD("Initiating pulling %d", tagId);
+ VLOG("Initiating pulling %d", tagId);
- if (mPullers.find(tagId) != mPullers.end()) {
- bool ret = mPullers.find(tagId)->second->Pull(data);
- ALOGD("pulled %d items", (int)data->size());
+ if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
+ bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+ VLOG("pulled %d items", (int)data->size());
return ret;
} else {
- ALOGD("Unknown tagId %d", tagId);
+ VLOG("Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
@@ -106,7 +140,7 @@
}
bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const {
- return mPullers.find(tagId) != mPullers.end();
+ return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
}
void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
@@ -195,6 +229,22 @@
}
}
+int StatsPullerManagerImpl::ForceClearPullerCache() {
+ int totalCleared = 0;
+ for (const auto& pulledAtom : kAllPullAtomInfo) {
+ totalCleared += pulledAtom.second.puller->ForceClearCache();
+ }
+ return totalCleared;
+}
+
+int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
+ int totalCleared = 0;
+ for (const auto& pulledAtom : kAllPullAtomInfo) {
+ totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
+ }
+ return totalCleared;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 7c59f66..76a4c14 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -32,6 +32,20 @@
namespace os {
namespace statsd {
+typedef struct {
+ // The field numbers of the fields that need to be summed when merging
+ // isolated uid with host uid.
+ std::vector<int> additiveFields;
+ // The field numbers of the fields that can't be merged when merging
+ // data belong to isolated uid and host uid.
+ std::vector<int> nonAdditiveFields;
+ // How long should the puller wait before doing an actual pull again. Default
+ // 1 sec. Set this to 0 if this is handled elsewhere.
+ long coolDownSec = 1;
+ // The actual puller
+ sp<StatsPuller> puller;
+} PullAtomInfo;
+
class StatsPullerManagerImpl : public virtual RefBase {
public:
static StatsPullerManagerImpl& GetInstance();
@@ -49,7 +63,13 @@
void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
-private:
+ int ForceClearPullerCache();
+
+ int ClearPullerCacheIfNecessary(long timestampSec);
+
+ const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+
+ private:
StatsPullerManagerImpl();
// use this to update alarm
@@ -57,9 +77,6 @@
sp<IStatsCompanionService> get_stats_companion_service();
- // mapping from simple matcher tagId to puller
- std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
typedef struct {
// pull_interval_sec : last_pull_time_sec
std::pair<uint64_t, uint64_t> timeInfo;
@@ -77,8 +94,6 @@
// bucket size. All pulled metrics start pulling based on this time, so that they can be
// correctly attributed to the correct buckets.
long mTimeBaseSec;
-
- LogEvent parse_pulled_data(String16 data);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index 6d12e25..65a1df0e 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <android/hardware/power/1.0/IPower.h>
@@ -99,6 +99,7 @@
auto statePtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE,
timestamp);
statePtr->write(state.name);
+ statePtr->write("");
statePtr->write(state.totalTransitions);
statePtr->write(state.residencyInMsecSinceBoot);
statePtr->init();
@@ -110,7 +111,8 @@
for (auto voter : state.voters) {
auto voterPtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE,
timestamp);
- voterPtr->write((std::string)state.name + "." + (std::string)voter.name);
+ voterPtr->write(state.name);
+ voterPtr->write(voter.name);
voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
voterPtr->init();
@@ -144,7 +146,8 @@
subsystem.states[j];
auto subsystemStatePtr = make_shared<LogEvent>(
android::util::SUBSYSTEM_SLEEP_STATE, timestamp);
- subsystemStatePtr->write((std::string)subsystem.name + "." + (std::string)state.name);
+ subsystemStatePtr->write(subsystem.name);
+ subsystemStatePtr->write(state.name);
subsystemStatePtr->write(state.totalTransitions);
subsystemStatePtr->write(state.residencyInMsecSinceBoot);
subsystemStatePtr->init();
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
new file mode 100644
index 0000000..7cfc1d48
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StatsPullerManagerImpl.h"
+#include "field_util.h"
+#include "puller_util.h"
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+using std::shared_ptr;
+using std::vector;
+
+DimensionsValue* getFieldValue(shared_ptr<LogEvent> event, int tagId, int fieldNum) {
+ Field field;
+ buildSimpleAtomField(tagId, fieldNum, &field);
+ return event->findFieldValueOrNull(field);
+}
+
+bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+ const vector<int>& nonAdditiveFields, int tagId) {
+ for (int f : nonAdditiveFields) {
+ DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+ DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+ if (!compareDimensionsValue(*lValue, *rValue)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// merge rhs to lhs
+void mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+ const vector<int>& additiveFields, int tagId) {
+ for (int f : additiveFields) {
+ DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+ DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+ if (lValue->has_value_int()) {
+ lValue->set_value_int(lValue->value_int() + rValue->value_int());
+ } else if (lValue->has_value_long()) {
+ lValue->set_value_long(lValue->value_long() + rValue->value_long());
+ }
+ }
+}
+
+// process all data and merge isolated with host if necessary
+void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId) {
+ if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
+ StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+ VLOG("Unknown pull atom id %d", tagId);
+ return;
+ }
+ if (android::util::kAtomsWithUidField.find(tagId) ==
+ android::util::kAtomsWithUidField.end()) {
+ VLOG("No uid to merge for atom %d", tagId);
+ return;
+ }
+ const vector<int>& additiveFields =
+ StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+ ->second.additiveFields;
+ const vector<int>& nonAdditiveFields =
+ StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+ ->second.nonAdditiveFields;
+
+ // map of host uid to isolated uid data index in the original vector.
+ // because of non additive fields, there could be multiple of them that can't
+ // be merged into one
+ map<int, vector<int>> hostToIsolated;
+ // map of host uid to their position in the original vector
+ map<int, vector<int>> hostPosition;
+ vector<int> isolatedUidPos;
+ // all uids in the original vector
+ vector<int> allUids;
+ for (size_t i = 0; i < data.size(); i++) {
+ // uid field is always first primitive filed, if present
+ DimensionsValue* uidField = getFieldValue(data[i], tagId, 1);
+ if (!uidField) {
+ VLOG("Bad data for %d, %s", tagId, data[i]->ToString().c_str());
+ return;
+ }
+ int uid = uidField->value_int();
+ allUids.push_back(uid);
+ const int hostUid = uidMap->getHostUidOrSelf(uid);
+ if (hostUid != uid) {
+ uidField->set_value_int(hostUid);
+ hostToIsolated[hostUid].push_back(i);
+ isolatedUidPos.push_back(i);
+ }
+ }
+ vector<shared_ptr<LogEvent>> mergedData;
+ for (size_t i = 0; i < allUids.size(); i++) {
+ if (hostToIsolated.find(allUids[i]) != hostToIsolated.end()) {
+ hostPosition[allUids[i]].push_back(i);
+ } else if (std::find(isolatedUidPos.begin(), isolatedUidPos.end(), i) != isolatedUidPos.end()) {
+ continue;
+ } else {
+ mergedData.push_back(data[i]);
+ }
+ }
+ for (auto iter = hostToIsolated.begin(); iter != hostToIsolated.end();
+ iter++) {
+ int uid = iter->first;
+ vector<int>& isolated = hostToIsolated[uid];
+ vector<int> toBeMerged;
+ toBeMerged.insert(toBeMerged.begin(), isolated.begin(), isolated.end());
+ if (hostPosition.find(uid) != hostPosition.end()) {
+ vector<int>& host = hostPosition[uid];
+ toBeMerged.insert(toBeMerged.end(), host.begin(), host.end());
+ }
+ vector<bool> used(toBeMerged.size());
+ for (size_t i = 0; i < toBeMerged.size(); i++) {
+ if (used[i] == true) {
+ continue;
+ }
+ for (size_t j = i + 1; j < toBeMerged.size(); j++) {
+ shared_ptr<LogEvent>& lhs = data[toBeMerged[i]];
+ shared_ptr<LogEvent>& rhs = data[toBeMerged[j]];
+ if (shouldMerge(lhs, rhs, nonAdditiveFields, tagId)) {
+ mergeEvent(lhs, rhs, additiveFields, tagId);
+ used[j] = true;
+ }
+ }
+ }
+ for (size_t i = 0; i < toBeMerged.size(); i++) {
+ if (used[i] == false) {
+ mergedData.push_back(data[i]);
+ }
+ }
+ }
+ data.clear();
+ data = mergedData;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
new file mode 100644
index 0000000..70d5321
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+#include "HashableDimensionKey.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp b/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp
index e1947c4..01c7587 100644
--- a/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp
+++ b/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <sstream>
@@ -63,7 +63,7 @@
size_t count;
if (info == nullptr || overallSize == 0 || infoSize == 0 ||
(count = overallSize / infoSize) == 0) {
- ALOGD("no malloc info, libc.debug.malloc.program property should be set");
+ VLOG("no malloc info, libc.debug.malloc.program property should be set");
return std::string();
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 77f5456..06c5b00 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsdStats.h"
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 1f4bfa6..f254327 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -85,6 +85,9 @@
// Maximum size of all files that can be written to stats directory on disk.
static const int kMaxFileSize = 50 * 1024 * 1024;
+ // How long to try to clear puller cache from last time
+ static const long kPullerCacheClearIntervalSec = 1;
+
/**
* Report a new config has been received and report the static stats about the config.
*
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 9e72f5b..e1ab5d5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -62,13 +62,14 @@
const char* buffer;
size_t len = android_log_write_list_buffer(mContext, &buffer);
// turns to reader mode
- mContext = create_android_log_parser(buffer, len);
- init(mContext);
- // destroy the context to save memory.
- if (mContext) {
+ android_log_context contextForRead = create_android_log_parser(buffer, len);
+ if (contextForRead) {
+ init(contextForRead);
+ // destroy the context to save memory.
// android_log_destroy will set mContext to NULL
- android_log_destroy(&mContext);
+ android_log_destroy(&contextForRead);
}
+ android_log_destroy(&mContext);
}
}
@@ -188,6 +189,9 @@
* of the elements that are written to the log.
*/
void LogEvent::init(android_log_context context) {
+ if (!context) {
+ return;
+ }
android_log_list_element elem;
// TODO: The log is actually structured inside one list. This is convenient
// because we'll be able to use it to put the attribution (WorkSource) block first
@@ -290,6 +294,28 @@
}
}
+int LogEvent::GetInt(size_t key, status_t* err) const {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
+ *err = BAD_INDEX;
+ return 0;
+ }
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueInt:
+ return leafValue->value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ case DimensionsValue::ValueCase::kValueBool:
+ case DimensionsValue::ValueCase::kValueFloat:
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
+ }
+}
+
const char* LogEvent::GetString(size_t key, status_t* err) const {
DimensionsValue value;
if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index eb2c008..d521e09 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -76,6 +76,7 @@
* Returns BAD_TYPE if the index is available but the data is the wrong type.
*/
int64_t GetLong(size_t key, status_t* err) const;
+ int GetInt(size_t key, status_t* err) const;
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index b6f440f..fae9172 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -129,43 +129,60 @@
}
bool matched = false;
switch (matcher.value_matcher_case()) {
- case FieldValueMatcher::ValueMatcherCase::kEqBool:
- // Logd does not support bool, it is int instead.
- matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
- break;
- case FieldValueMatcher::ValueMatcherCase::kEqString:
- {
- if (IsAttributionUidField(*rootField)) {
- const int uid = ret.first->second.value_int();
- std::set<string> packageNames =
+ case FieldValueMatcher::ValueMatcherCase::kEqBool: {
+ // Logd does not support bool, it is int instead.
+ matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqString: {
+ if (IsAttributionUidField(*rootField)) {
+ const int uid = ret.first->second.value_int();
+ std::set<string> packageNames =
uidMap.getAppNamesFromUid(uid, true /* normalize*/);
- matched = packageNames.find(matcher.eq_string()) != packageNames.end();
- } else {
- matched = (ret.first->second.value_str() == matcher.eq_string());
- }
- }
- break;
- case FieldValueMatcher::ValueMatcherCase::kEqInt:
- matched = (ret.first->second.value_int() == matcher.eq_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kLtInt:
- matched = (ret.first->second.value_int() < matcher.lt_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kGtInt:
- matched = (ret.first->second.value_int() > matcher.gt_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kLtFloat:
- matched = (ret.first->second.value_float() < matcher.lt_float());
- break;
- case FieldValueMatcher::ValueMatcherCase::kGtFloat:
- matched = (ret.first->second.value_float() > matcher.gt_float());
- break;
- case FieldValueMatcher::ValueMatcherCase::kLteInt:
- matched = (ret.first->second.value_int() <= matcher.lte_int());
- break;
- case FieldValueMatcher::ValueMatcherCase::kGteInt:
- matched = (ret.first->second.value_int() >= matcher.gte_int());
- break;
+ matched = packageNames.find(matcher.eq_string()) != packageNames.end();
+ } else {
+ matched = (ret.first->second.value_str() == matcher.eq_string());
+ }
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kEqInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val == matcher.eq_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLtInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val < matcher.lt_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGtInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val > matcher.gt_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
+ matched = (ret.first->second.value_float() < matcher.lt_float());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
+ matched = (ret.first->second.value_float() > matcher.gt_float());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kLteInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val <= matcher.lte_int());
+ break;
+ }
+ case FieldValueMatcher::ValueMatcherCase::kGteInt: {
+ int64_t val = ret.first->second.has_value_int() ?
+ ret.first->second.value_int() : ret.first->second.value_long();
+ matched = (val >= matcher.gte_int());
+ break;
+ }
default:
break;
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0455f6a..5a042b6 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,6 +21,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
+#include "dimension.h"
#include <limits.h>
#include <stdlib.h>
@@ -44,8 +45,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_COUNT_METRICS = 5;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -71,13 +70,15 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -94,12 +95,14 @@
void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
report->set_metric_id(mMetricId);
- report->set_start_report_nanos(mStartTimeNs);
auto count_metrics = report->mutable_count_metrics();
for (const auto& counter : mPastBuckets) {
CountMetricData* metricData = count_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ counter.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ counter.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : counter.second) {
CountBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -117,23 +120,31 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
VLOG("metric %lld dump report now...",(long long)mMetricId);
for (const auto& counter : mPastBuckets) {
- const HashableDimensionKey& hashableKey = counter.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = counter.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
- long long dimensionToken = protoOutput->start(
+ long long dimensionInWhatToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
- protoOutput->end(dimensionToken);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInWhatToken);
+
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
@@ -152,10 +163,8 @@
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
- mStartTimeNs = mCurrentBucketStartTimeNs;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
@@ -166,7 +175,7 @@
mCondition = conditionMet;
}
-bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
return false;
}
@@ -187,7 +196,7 @@
}
void CountMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
@@ -199,13 +208,11 @@
}
auto it = mCurrentSlicedCounter->find(eventKey);
-
if (it == mCurrentSlicedCounter->end()) {
// ===========GuardRail==============
if (hitGuardRailLocked(eventKey)) {
return;
}
-
// create a counter for the new key
(*mCurrentSlicedCounter)[eventKey] = 1;
} else {
@@ -213,10 +220,14 @@
auto& count = it->second;
count++;
}
-
for (auto& tracker : mAnomalyTrackers) {
+ int64_t countWholeBucket = mCurrentSlicedCounter->find(eventKey)->second;
+ auto prev = mCurrentFullCounters->find(eventKey);
+ if (prev != mCurrentFullCounters->end()) {
+ countWholeBucket += prev->second;
+ }
tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
- mCurrentSlicedCounter->find(eventKey)->second);
+ countWholeBucket);
}
VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(),
@@ -226,33 +237,65 @@
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
void CountMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
- if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
+ uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
+ if (eventTimeNs < currentBucketEndTimeNs) {
return;
}
+ flushCurrentBucketLocked(eventTimeNs);
+ // Setup the bucket start time and number.
+ uint64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+ VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
+ (long long)mCurrentBucketStartTimeNs);
+}
+
+void CountMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) {
+ uint64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ if (eventTimeNs < fullBucketEndTimeNs) {
+ info.mBucketEndNs = eventTimeNs;
+ } else {
+ info.mBucketEndNs = fullBucketEndTimeNs;
+ }
info.mBucketNum = mCurrentBucketNum;
for (const auto& counter : *mCurrentSlicedCounter) {
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
- VLOG("metric %lld, dump key value: %s -> %lld",
- (long long)mMetricId, counter.first.c_str(), (long long)counter.second);
+ VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId, counter.first.c_str(),
+ (long long)counter.second);
}
- for (auto& tracker : mAnomalyTrackers) {
- tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
+ // If we have finished a full bucket, then send this to anomaly tracker.
+ if (eventTimeNs > fullBucketEndTimeNs) {
+ // Accumulate partial buckets with current value and then send to anomaly tracker.
+ if (mCurrentFullCounters->size() > 0) {
+ for (const auto& keyValuePair : *mCurrentSlicedCounter) {
+ (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second;
+ }
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addPastBucket(mCurrentFullCounters, mCurrentBucketNum);
+ }
+ mCurrentFullCounters = std::make_shared<DimToValMap>();
+ } else {
+ // Skip aggregating the partial buckets since there's no previous partial bucket.
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
+ }
+ }
+ } else {
+ // Accumulate partial bucket.
+ for (const auto& keyValuePair : *mCurrentSlicedCounter) {
+ (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second;
+ }
}
- // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
+ // Only resets the counters, but doesn't setup the times nor numbers.
+ // (Do not clear since the old one is still referenced in mAnomalyTrackers).
mCurrentSlicedCounter = std::make_shared<DimToValMap>();
- uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- mCurrentBucketNum += numBucketsForward;
- VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
}
// Rough estimate of CountMetricProducer buffer stored. This number will be
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 061b7a3..b06c77b 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -50,7 +50,7 @@
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -71,22 +71,30 @@
void dumpStatesLocked(FILE* out, bool verbose) const override{};
// Util function to flush the old packet.
- void flushIfNeededLocked(const uint64_t& newEventTime);
+ void flushIfNeededLocked(const uint64_t& newEventTime) override;
+
+ void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override;
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
- // The current bucket.
+ // The current bucket (may be a partial bucket).
std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
+ // The sum of previous partial buckets in the current full bucket (excluding the current
+ // partial bucket). This is only updated while flushing the current bucket.
+ std::shared_ptr<DimToValMap> mCurrentFullCounters = std::make_shared<DimToValMap>();
+
static const size_t kBucketSize = sizeof(CountBucket{});
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
+ FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
+ FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 000874c..65cbc4a 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -21,6 +21,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
+#include "dimension.h"
#include <limits.h>
#include <stdlib.h>
@@ -43,8 +44,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_DURATION_METRICS = 6;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -81,13 +80,15 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -113,26 +114,52 @@
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- const HashableDimensionKey& eventKey) const {
+ const MetricDimensionKey& eventKey) const {
switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
- mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+ mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
+ mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
- mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+ mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
+ mStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
}
}
void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
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 : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->onSlicedConditionMayChange(eventTime);
}
+
+
+ std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+ ConditionState conditionState = mWizard->getMetConditionDimension(
+ mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet);
+
+ bool condition = (conditionState == ConditionState::kTrue);
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+ conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+ }
+ std::unordered_set<MetricDimensionKey> 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);
+ }
+ newKeys.insert(newKey);
+ }
+ }
}
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
@@ -142,7 +169,7 @@
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 : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -150,12 +177,14 @@
void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
report->set_metric_id(mMetricId);
- report->set_start_report_nanos(mStartTimeNs);
auto duration_metrics = report->mutable_duration_metrics();
for (const auto& pair : mPastBuckets) {
DurationMetricData* metricData = duration_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ pair.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ pair.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : pair.second) {
auto bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -173,14 +202,13 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
VLOG("metric %lld dump report now...", (long long)mMetricId);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = pair.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -188,9 +216,18 @@
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
+
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
long long bucketInfoToken = protoOutput->start(
@@ -209,53 +246,66 @@
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
- mStartTimeNs = mCurrentBucketStartTimeNs;
}
-void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
+ uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
+
+ if (currentBucketEndTimeNs > eventTimeNs) {
return;
}
VLOG("flushing...........");
- for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
- if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
+ 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 = mCurrentSlicedDuration.erase(it);
+ it = mCurrentSlicedDurationTrackerMap.erase(it);
} else {
++it;
}
}
- int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+ int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
}
+void DurationMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) {
+ for (auto it = mCurrentSlicedDurationTrackerMap.begin();
+ it != mCurrentSlicedDurationTrackerMap.end();) {
+ if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+ VLOG("erase bucket for key %s", it->first.c_str());
+ it = mCurrentSlicedDurationTrackerMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedDuration.size() == 0) {
+ if (mCurrentSlicedDurationTrackerMap.size() == 0) {
return;
}
fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedDuration.size());
+ (unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
- for (const auto& slice : mCurrentSlicedDuration) {
+ for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
fprintf(out, "\t%s\n", slice.first.c_str());
slice.second->dumpStates(out, verbose);
}
}
}
-bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// the key is not new, we are good.
- if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+ if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+ if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -268,27 +318,26 @@
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) {
flushIfNeededLocked(event.GetTimestampNs());
if (matcherIndex == mStopAllIndex) {
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->noteStopAll(event.GetTimestampNs());
}
return;
}
-
- if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+ if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
+ mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
}
- auto it = mCurrentSlicedDuration.find(eventKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
std::vector<DimensionsValue> values;
getDimensionKeys(event, mInternalDimensions, &values);
@@ -302,10 +351,11 @@
} else {
for (const DimensionsValue& value : values) {
if (matcherIndex == mStartIndex) {
- it->second->noteStart(HashableDimensionKey(value), condition,
- event.GetTimestampNs(), conditionKeys);
+ it->second->noteStart(
+ HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys);
} else if (matcherIndex == mStopIndex) {
- it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+ it->second->noteStop(
+ HashableDimensionKey(value), event.GetTimestampNs(), false);
}
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index d8cab92..a496016 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -50,7 +50,7 @@
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) override;
@@ -73,6 +73,8 @@
// Util function to flush the old packet.
void flushIfNeededLocked(const uint64_t& eventTime);
+ void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override;
+
const DurationMetric_AggregationType mAggregationType;
// Index of the SimpleAtomMatcher which defines the start.
@@ -92,26 +94,30 @@
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
- mCurrentSlicedDuration;
+ std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+ mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
- const HashableDimensionKey& eventKey) const;
+ const MetricDimensionKey& eventKey) const;
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(DurationBucket{});
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
+ FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
+ FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
+ FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
+ FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
};
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 25c86d0..0578e06 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -42,8 +42,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_EVENT_METRICS = 4;
// for EventMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -106,8 +104,6 @@
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
size_t bufferSize = mProto->size();
VLOG("metric %lld dump report now... proto size: %zu ",
@@ -118,7 +114,6 @@
reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
startNewProtoOutputStreamLocked();
- mStartTimeNs = dumpTimeNs;
}
void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
@@ -128,7 +123,7 @@
}
void EventMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (!condition) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 9da0dd0..935f206 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -45,7 +45,7 @@
private:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1072c5a..62ee6ef 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -45,8 +45,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_GAUGE_METRICS = 8;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -82,13 +80,15 @@
mFieldFilter = metric.gauge_fields_filter();
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
@@ -132,22 +132,30 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
+ const MetricDimensionKey& dimensionKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
+
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
long long bucketInfoToken = protoOutput->start(
@@ -177,13 +185,22 @@
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
- mStartTimeNs = mCurrentBucketStartTimeNs;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
+void GaugeMetricProducer::pullLocked() {
+ vector<std::shared_ptr<LogEvent>> allData;
+ if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
+ ALOGE("Stats puller failed for tag: %d", mPullTagId);
+ return;
+ }
+ for (const auto& data : allData) {
+ onMatchedLogEventLocked(0, *data);
+ }
+}
+
void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
@@ -248,7 +265,7 @@
}
}
-bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
return false;
}
@@ -268,7 +285,7 @@
}
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (condition == false) {
@@ -315,7 +332,6 @@
}
void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() {
- mCurrentSlicedBucketForAnomaly->clear();
status_t err = NO_ERROR;
for (const auto& slice : *mCurrentSlicedBucket) {
if (slice.second.empty() || slice.second.front().mFields->empty()) {
@@ -338,42 +354,57 @@
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
- if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
+ uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
+
+ if (eventTimeNs < currentBucketEndTimeNs) {
VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
(long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
return;
}
+ flushCurrentBucketLocked(eventTimeNs);
+
+ // Adjusts the bucket start and end times.
+ int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+ VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
+ (long long)mCurrentBucketStartTimeNs);
+}
+
+void GaugeMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) {
+ uint64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ if (eventTimeNs < fullBucketEndTimeNs) {
+ info.mBucketEndNs = eventTimeNs;
+ } else {
+ info.mBucketEndNs = fullBucketEndTimeNs;
+ }
info.mBucketNum = mCurrentBucketNum;
for (const auto& slice : *mCurrentSlicedBucket) {
info.mGaugeAtoms = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- VLOG("gauge metric %lld, dump key value: %s",
- (long long)mMetricId, slice.first.c_str());
+ VLOG("gauge metric %lld, dump key value: %s", (long long)mMetricId, slice.first.c_str());
}
- // Reset counters
+ // If we have anomaly trackers, we need to update the partial bucket values.
if (mAnomalyTrackers.size() > 0) {
updateCurrentSlicedBucketForAnomaly();
- for (auto& tracker : mAnomalyTrackers) {
- tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum);
+
+ if (eventTimeNs > fullBucketEndTimeNs) {
+ // This is known to be a full bucket, so send this data to the anomaly tracker.
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum);
+ }
+ mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
}
}
- mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
-
- // Adjusts the bucket start time
- int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- mCurrentBucketNum += numBucketsForward;
- VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
}
size_t GaugeMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6c01347..d5d34be 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -44,7 +44,7 @@
uint64_t mBucketNum;
};
-typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>>
+typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
DimToGaugeAtomsMap;
// This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -62,9 +62,25 @@
// Handles when the pulled data arrives.
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
+ // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
+ void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (eventTimeNs > getCurrentBucketEndTimeNs()) {
+ // Flush full buckets on the normal path up to the latest bucket boundary.
+ flushIfNeededLocked(eventTimeNs);
+ }
+ flushCurrentBucketLocked(eventTimeNs);
+ mCurrentBucketStartTimeNs = eventTimeNs;
+ if (mPullTagId != -1) {
+ pullLocked();
+ }
+ };
+
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -91,7 +107,11 @@
void dumpStatesLocked(FILE* out, bool verbose) const override{};
// Util function to flush the old packet.
- void flushIfNeededLocked(const uint64_t& eventTime);
+ void flushIfNeededLocked(const uint64_t& eventTime) override;
+
+ void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override;
+
+ void pullLocked();
std::shared_ptr<StatsPullerManager> mStatsPullerManager;
// tagId for pulled data. -1 if this is not pulled
@@ -99,15 +119,17 @@
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
- // The current bucket.
+ // The current partial bucket.
std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
- // The current bucket for anomaly detection.
+ // The current full bucket for anomaly detection. This is updated to the latest value seen for
+ // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket).
std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
- // Translate Atom based bucket to single numeric value bucket for anomaly
+ // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map
+ // for each slice with the latest value.
void updateCurrentSlicedBucketForAnomaly();
// Whitelist of fields to report. Empty means all are reported.
@@ -119,12 +141,14 @@
std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(GaugeBucket{});
FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
};
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index e74924a..85e655b 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -15,6 +15,8 @@
*/
#include "MetricProducer.h"
+#include "dimension.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -30,29 +32,51 @@
bool condition;
ConditionKey conditionKey;
+
+ std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mConditionLinks) {
getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
}
- if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
- condition = false;
- } else {
- condition = true;
- }
+ auto conditionState =
+ mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+ &dimensionKeysInCondition);
+ condition = (conditionState == ConditionState::kTrue);
} else {
condition = mCondition;
}
- if (mDimensions.has_field() && mDimensions.child_size() > 0) {
- vector<DimensionsValue> dimensionValues;
- getDimensionKeys(event, mDimensions, &dimensionValues);
- for (const DimensionsValue& dimensionValue : dimensionValues) {
+ vector<DimensionsValue> dimensionInWhatValues;
+ if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) {
+ getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues);
+ }
+
+ if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
+ } else if (dimensionKeysInCondition.empty()) {
+ for (const DimensionsValue& whatValue : dimensionInWhatValues) {
onMatchedLogEventInternalLocked(
- matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
+ matcherIndex,
+ MetricDimensionKey(HashableDimensionKey(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);
}
} else {
- onMatchedLogEventInternalLocked(
- matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event);
+ for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+ for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex,
+ MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey),
+ conditionKey, condition, event);
+ }
+ }
}
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 6f33073..542dd8a 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -53,15 +53,32 @@
virtual ~MetricProducer(){};
- void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override{
+ /**
+ * Forces this metric to split into a partial bucket right now. If we're past a full bucket, we
+ * first call the standard flushing code to flush up to the latest full bucket. Then we call
+ * the flush again when the end timestamp is forced to be now, and then after flushing, update
+ * the start timestamp to be now.
+ */
+ void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (eventTimeNs > getCurrentBucketEndTimeNs()) {
+ // Flush full buckets on the normal path up to the latest bucket boundary.
+ flushIfNeededLocked(eventTimeNs);
+ }
+ // Now flush a partial bucket.
+ flushCurrentBucketLocked(eventTimeNs);
+ mCurrentBucketStartTimeNs = eventTimeNs;
+ // Don't update the current bucket number so that the anomaly tracker knows this bucket
+ // is a partial bucket and can merge it with the previous bucket.
+ };
+
+ void notifyAppRemoved(const uint64_t& eventTimeNs, const string& apk, const int uid) override{
// TODO: Implement me.
};
- void notifyAppRemoved(const string& apk, const int uid) override{
- // TODO: Implement me.
- };
-
- void onUidMapReceived() override{
+ void onUidMapReceived(const uint64_t& eventTimeNs) override{
// TODO: Implement me.
};
@@ -87,10 +104,12 @@
};
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
+ // This method clears all the past buckets.
void onDumpReport(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) {
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, protoOutput);
}
+ // This method does not clear the past buckets.
void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, report);
@@ -135,15 +154,43 @@
virtual size_t byteSizeLocked() const = 0;
virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
+ /**
+ * Flushes the current bucket if the eventTime is after the current bucket's end time.
+ */
+ virtual void flushIfNeededLocked(const uint64_t& eventTime){};
+
+ /**
+ * For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
+ * we need to be able to flush the current buckets on demand (ie, end the current bucket and
+ * start new bucket). If this function is called when eventTimeNs is greater than the current
+ * bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise,
+ * we assume that we want to flush a partial bucket. The bucket start timestamp and bucket
+ * number are not changed by this function. This method should only be called by
+ * flushIfNeededLocked or the app upgrade handler; the caller MUST update the bucket timestamp
+ * and bucket number as needed.
+ */
+ virtual void flushCurrentBucketLocked(const uint64_t& eventTimeNs){};
+
+ // Convenience to compute the current bucket's end time, which is always aligned with the
+ // start time of the metric.
+ uint64_t getCurrentBucketEndTimeNs() {
+ return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
+ }
+
const int64_t mMetricId;
const ConfigKey mConfigKey;
- // The start time for the current in memory metrics data.
+ // The time when this metric producer was first created. The end time for the current bucket
+ // can be computed from this based on mCurrentBucketNum.
uint64_t mStartTimeNs;
+ // Start time may not be aligned with the start of statsd if there is an app upgrade in the
+ // middle of a bucket.
uint64_t mCurrentBucketStartTimeNs;
+ // Used by anomaly detector to track which bucket we are in. This is not sent with the produced
+ // report.
uint64_t mCurrentBucketNum;
int64_t mBucketSizeNs;
@@ -156,7 +203,8 @@
int mConditionTrackerIndex;
- FieldMatcher mDimensions; // The dimension defined in statsd_config
+ FieldMatcher mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
+ FieldMatcher mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
std::vector<MetricConditionLink> mConditionLinks;
@@ -178,7 +226,7 @@
* [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
*/
virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index d0737de..6c21b05 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "MetricsManager.h"
#include "statslog.h"
@@ -28,6 +28,8 @@
#include "stats_util.h"
#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+#include <utils/SystemClock.h>
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_MESSAGE;
@@ -47,7 +49,7 @@
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
const long timeBaseSec, sp<UidMap> uidMap)
- : mConfigKey(key), mUidMap(uidMap) {
+ : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(0) {
mConfigValid =
initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
@@ -59,8 +61,9 @@
// mConfigValid = false;
// ALOGE("Log source white list is empty! This config won't get any data.");
- mAllowedUid.push_back(1000);
- mAllowedUid.push_back(0);
+ mAllowedUid.push_back(AID_ROOT);
+ mAllowedUid.push_back(AID_STATSD);
+ mAllowedUid.push_back(AID_SYSTEM);
mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
} else {
for (const auto& source : config.allowed_log_source()) {
@@ -119,7 +122,8 @@
return mConfigValid;
}
-void MetricsManager::notifyAppUpgrade(const string& apk, const int uid, const int64_t version) {
+void MetricsManager::notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) {
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
return;
@@ -129,7 +133,8 @@
initLogSourceWhiteList();
}
-void MetricsManager::notifyAppRemoved(const string& apk, const int uid) {
+void MetricsManager::notifyAppRemoved(const uint64_t& eventTimeNs, const string& apk,
+ const int uid) {
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
return;
@@ -139,7 +144,7 @@
initLogSourceWhiteList();
}
-void MetricsManager::onUidMapReceived() {
+void MetricsManager::onUidMapReceived(const uint64_t& eventTimeNs) {
if (mAllowedPkg.size() == 0) {
return;
}
@@ -180,6 +185,7 @@
protoOutput->end(token);
}
}
+ mLastReportTimeNs = ::android::elapsedRealtimeNano();
VLOG("=========================Metric Reports End==========================");
}
@@ -189,28 +195,47 @@
return;
}
- if (event.GetTagId() != android::util::APP_HOOK) {
+ if (event.GetTagId() == android::util::APP_HOOK) { // Check that app hook fields are valid.
+ // TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
+ status_t err = NO_ERROR;
+
+ // 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);
+ 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);
+ 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);
+ 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);
+ 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;
+ long duration = event.GetLong(event.size(), &err);
+ if (err != NO_ERROR || duration > 100000) {
+ VLOG("Davey duration is unreasonably long: %ld", duration);
+ return;
+ }
+ } else {
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
VLOG("log source %d not on the whitelist", event.GetUid());
return;
}
- } else { // Check that app hook fields are valid.
- // TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
-
- // Label is 2nd from last field and must be from [0, 15].
- status_t err = NO_ERROR;
- long label = event.GetLong(event.size()-1, &err);
- if (err != NO_ERROR || label < 0 || label > 15) {
- VLOG("App hook does not have valid label %ld", label);
- 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("App hook does not have valid state %ld", apphookState);
- return;
- }
}
int tagId = event.GetTagId();
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 9cdbafc..2b30f44 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -51,11 +51,12 @@
void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
- void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override;
+ void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) override;
- void notifyAppRemoved(const string& apk, const int uid) override;
+ void notifyAppRemoved(const uint64_t& eventTimeNs, const string& apk, const int uid) override;
- void onUidMapReceived() override;
+ void onUidMapReceived(const uint64_t& eventTimeNs) override;
bool shouldAddUidMapListener() const {
return !mAllowedPkg.empty();
@@ -63,6 +64,11 @@
void dumpStates(FILE* out, bool verbose);
+ // Returns the elapsed realtime when this metric manager last reported metrics.
+ uint64_t getLastReportTimeNs() {
+ return mLastReportTimeNs;
+ };
+
// Config source owner can call onDumpReport() to get all the metrics collected.
virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
@@ -77,6 +83,8 @@
bool mConfigValid = false;
+ uint64_t mLastReportTimeNs;
+
// The uid log sources from StatsdConfig.
std::vector<int32_t> mAllowedUid;
@@ -143,6 +151,10 @@
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);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ae0c673..7b1944c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -48,8 +48,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_VALUE_METRICS = 7;
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -81,13 +79,15 @@
}
mBucketSizeNs = bucketSizeMills * 1000000;
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
if (!metric.has_condition() && mPullTagId != -1) {
VLOG("Setting up periodic pulling for %d", mPullTagId);
@@ -120,11 +120,13 @@
void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
report->set_metric_id(mMetricId);
- report->set_start_report_nanos(mStartTimeNs);
auto value_metrics = report->mutable_value_metrics();
for (const auto& pair : mPastBuckets) {
ValueMetricData* metricData = value_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ pair.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ pair.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : pair.second) {
ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -142,20 +144,27 @@
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = pair.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
@@ -173,11 +182,9 @@
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
VLOG("metric %lld dump report now...", (long long)mMetricId);
mPastBuckets.clear();
- mStartTimeNs = mCurrentBucketStartTimeNs;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
@@ -239,7 +246,7 @@
}
}
-bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -260,7 +267,7 @@
}
void ValueMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
@@ -307,8 +314,13 @@
interval.sum += value;
}
+ long wholeBucketVal = interval.sum;
+ auto prev = mCurrentFullBucket.find(eventKey);
+ if (prev != mCurrentFullBucket.end()) {
+ wholeBucketVal += prev->second;
+ }
for (auto& tracker : mAnomalyTrackers) {
- tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, interval.sum);
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal);
}
}
@@ -320,16 +332,39 @@
}
void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
+ uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
+
+ if (currentBucketEndTimeNs > eventTimeNs) {
VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
- (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+ (long long)(currentBucketEndTimeNs));
return;
}
+
+ flushCurrentBucketLocked(eventTimeNs);
+
+ int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+
+ if (numBucketsForward > 1) {
+ VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
+ }
+ VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
+ (long long)mCurrentBucketStartTimeNs);
+}
+
+void ValueMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) {
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
(int)mCurrentSlicedBucket.size());
+ uint64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+
ValueBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ if (eventTimeNs < fullBucketEndTimeNs) {
+ info.mBucketEndNs = eventTimeNs;
+ } else {
+ info.mBucketEndNs = fullBucketEndTimeNs;
+ }
info.mBucketNum = mCurrentBucketNum;
int tainted = 0;
@@ -339,27 +374,42 @@
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
-
- for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr) {
- tracker->addPastBucket(slice.first, info.mValue, info.mBucketNum);
- }
- }
}
VLOG("%d tainted pairs in the bucket", tainted);
+ if (eventTimeNs > fullBucketEndTimeNs) { // If full bucket, send to anomaly tracker.
+ // Accumulate partial buckets with current value and then send to anomaly tracker.
+ if (mCurrentFullBucket.size() > 0) {
+ for (const auto& slice : mCurrentSlicedBucket) {
+ mCurrentFullBucket[slice.first] += slice.second.sum;
+ }
+ for (const auto& slice : mCurrentFullBucket) {
+ for (auto& tracker : mAnomalyTrackers) {
+ if (tracker != nullptr) {
+ tracker->addPastBucket(slice.first, slice.second, mCurrentBucketNum);
+ }
+ }
+ }
+ mCurrentFullBucket.clear();
+ } else {
+ // Skip aggregating the partial buckets since there's no previous partial bucket.
+ for (const auto& slice : mCurrentSlicedBucket) {
+ for (auto& tracker : mAnomalyTrackers) {
+ if (tracker != nullptr) {
+ tracker->addPastBucket(slice.first, slice.second.sum, mCurrentBucketNum);
+ }
+ }
+ }
+ }
+ } else {
+ // Accumulate partial bucket.
+ for (const auto& slice : mCurrentSlicedBucket) {
+ mCurrentFullBucket[slice.first] += slice.second.sum;
+ }
+ }
+
// Reset counters
mCurrentSlicedBucket.clear();
-
- int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- mCurrentBucketNum += numBucketsForward;
-
- if (numBucketsForward > 1) {
- VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
- }
- VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
}
size_t ValueMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 9f750cf..bf5b7df 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -47,9 +47,42 @@
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
+ // ValueMetric needs special logic if it's a pulled atom.
+ void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mPullTagId != -1) {
+ vector<shared_ptr<LogEvent>> allData;
+ mStatsPullerManager->Pull(mPullTagId, &allData);
+ if (allData.size() == 0) {
+ // This shouldn't happen since this valuemetric is not useful now.
+ }
+
+ // Pretend the pulled data occurs right before the app upgrade event.
+ mCondition = false;
+ for (const auto& data : allData) {
+ data->setTimestampNs(eventTimeNs - 1);
+ onMatchedLogEventLocked(0, *data);
+ }
+
+ flushCurrentBucketLocked(eventTimeNs);
+ mCurrentBucketStartTimeNs = eventTimeNs;
+
+ mCondition = true;
+ for (const auto& data : allData) {
+ data->setTimestampNs(eventTimeNs);
+ onMatchedLogEventLocked(0, *data);
+ }
+ } else { // For pushed value metric, we simply flush and reset the current bucket start.
+ flushCurrentBucketLocked(eventTimeNs);
+ mCurrentBucketStartTimeNs = eventTimeNs;
+ }
+ };
+
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -70,7 +103,9 @@
void dumpStatesLocked(FILE* out, bool verbose) const override{};
// Util function to flush the old packet.
- void flushIfNeededLocked(const uint64_t& eventTime);
+ void flushIfNeededLocked(const uint64_t& eventTime) override;
+
+ void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override;
const FieldMatcher mValueField;
@@ -99,21 +134,25 @@
long sum;
} Interval;
- std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+ std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
+
+ std::unordered_map<MetricDimensionKey, long> mCurrentFullBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(ValueBucket{});
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
};
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index c2d2cea..356a81c 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -60,9 +60,11 @@
class DurationTracker {
public:
- DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
+ DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs,
+ uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
: mConfigKey(key),
mTrackerId(id),
@@ -70,15 +72,20 @@
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
mBucketSizeNs(bucketSizeNs),
+ mDimensionInCondition(dimensionInCondition),
mNested(nesting),
mCurrentBucketStartTimeNs(currentBucketStartNs),
mDuration(0),
- mCurrentBucketNum(0),
+ mDurationFullBucket(0),
+ mCurrentBucketNum(currentBucketNum),
+ mStartTimeNs(startTimeNs),
mConditionSliced(conditionSliced),
mAnomalyTrackers(anomalyTrackers){};
virtual ~DurationTracker(){};
+ virtual unique_ptr<DurationTracker> clone(const uint64_t eventTime) = 0;
+
virtual void noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -92,7 +99,13 @@
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
+
+ // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
+ // an app upgrade, we assume that we're trying to form a partial bucket.
+ virtual bool flushCurrentBucket(
+ const uint64_t& eventTimeNs,
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
// Predict the anomaly timestamp given the current status.
virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
@@ -100,6 +113,10 @@
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
+ void setEventKey(const MetricDimensionKey& eventKey) {
+ mEventKey = eventKey;
+ }
+
protected:
// Starts the anomaly alarm.
void startAnomalyAlarm(const uint64_t eventTime) {
@@ -145,12 +162,19 @@
}
}
}
+
+ // Convenience to compute the current bucket's end time, which is always aligned with the
+ // start time of the metric.
+ uint64_t getCurrentBucketEndTimeNs() {
+ return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
+ }
+
// A reference to the DurationMetricProducer's config key.
const ConfigKey& mConfigKey;
const int64_t mTrackerId;
- HashableDimensionKey mEventKey;
+ MetricDimensionKey mEventKey;
sp<ConditionWizard> mWizard;
@@ -158,14 +182,20 @@
const int64_t mBucketSizeNs;
+ const FieldMatcher mDimensionInCondition;
+
const bool mNested;
uint64_t mCurrentBucketStartTimeNs;
- int64_t mDuration; // current recorded duration result
+ int64_t mDuration; // current recorded duration result (for partial bucket)
+
+ int64_t mDurationFullBucket; // Sum of past partial buckets in current full bucket.
uint64_t mCurrentBucketNum;
+ const uint64_t mStartTimeNs;
+
const bool mConditionSliced;
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 412a0c9..c3bafc6 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -25,13 +25,25 @@
namespace statsd {
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t currentBucketNum,
+ uint64_t startTimeNs, uint64_t bucketSizeNs,
bool conditionSliced,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- bucketSizeNs, conditionSliced, anomalyTrackers) {
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+ currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
+ conditionSliced, anomalyTrackers) {
+}
+
+unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) {
+ auto clonedTracker = make_unique<MaxDurationTracker>(*this);
+ for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) {
+ it->second.lastStartTime = eventTime;
+ it->second.lastDuration = 0;
+ }
+ return clonedTracker;
}
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -44,7 +56,7 @@
if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mInfos.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+ mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -92,7 +104,6 @@
void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
bool forceStop) {
- declareAnomalyIfAlarmExpired(eventTime);
VLOG("MaxDuration: key %s stop", key.c_str());
if (mInfos.find(key) == mInfos.end()) {
// we didn't see a start event before. do nothing.
@@ -112,7 +123,7 @@
VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(),
(long long)duration.lastStartTime, (long long)eventTime,
(long long)durationTime);
- duration.lastDuration = duration.lastDuration + durationTime;
+ duration.lastDuration += durationTime;
VLOG(" record duration: %lld ", (long long)duration.lastDuration);
}
break;
@@ -128,7 +139,6 @@
if (duration.lastDuration > mDuration) {
mDuration = duration.lastDuration;
- detectAndDeclareAnomaly(eventTime, mCurrentBucketNum, mDuration);
VLOG("Max: new max duration: %lld", (long long)mDuration);
}
// Once an atom duration ends, we erase it. Next time, if we see another atom event with the
@@ -148,96 +158,81 @@
}
}
-bool MaxDurationTracker::flushIfNeeded(
- uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
- return false;
- }
-
+bool MaxDurationTracker::flushCurrentBucket(
+ const uint64_t& eventTimeNs,
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
VLOG("MaxDurationTracker flushing.....");
// adjust the bucket start time
- int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ int numBucketsForward = 0;
+ uint64_t fullBucketEnd = getCurrentBucketEndTimeNs();
+ uint64_t currentBucketEndTimeNs;
+ if (eventTimeNs >= fullBucketEnd) {
+ numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs;
+ currentBucketEndTimeNs = fullBucketEnd;
+ } else {
+ // This must be a partial bucket.
+ currentBucketEndTimeNs = eventTimeNs;
+ }
- uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
-
- DurationBucket info;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = endTime;
- info.mBucketNum = mCurrentBucketNum;
-
- uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
-
- bool hasOnGoingStartedEvent = false; // a kStarted event last across bucket boundaries.
bool hasPendingEvent =
false; // has either a kStarted or kPaused event across bucket boundaries
- // meaning we need to carry them over to the new bucket.
+ // meaning we need to carry them over to the new bucket.
for (auto it = mInfos.begin(); it != mInfos.end(); ++it) {
int64_t finalDuration = it->second.lastDuration;
- if (it->second.state == kStarted) {
- // the event is still on-going, duration needs to be updated.
- // |..lastDurationTime_recorded...last_start -----|bucket_end. We need to record the
- // duration between lastStartTime and bucketEnd.
- int64_t durationTime = endTime - it->second.lastStartTime;
-
- finalDuration += durationTime;
- VLOG(" unrecorded %lld -> %lld", (long long)(durationTime), (long long)finalDuration);
- // if the event is still on-going, we need to fill the buckets between prev_bucket and
- // now_bucket. |prev_bucket|...|..|...|now_bucket|
- hasOnGoingStartedEvent = true;
- }
-
- if (finalDuration > mDuration) {
- mDuration = finalDuration;
- }
-
if (it->second.state == DurationState::kStopped) {
// No need to keep buckets for events that were stopped before.
mInfos.erase(it);
} else {
hasPendingEvent = true;
- // for kPaused, and kStarted event, we will keep track of them, and reset the start time
- // and duration.
- it->second.lastStartTime = mCurrentBucketStartTimeNs;
- it->second.lastDuration = 0;
}
}
+ // mDuration is updated in noteStop to the maximum duration that ended in the current bucket.
if (mDuration != 0) {
+ DurationBucket info;
+ info.mBucketStartNs = mCurrentBucketStartTimeNs;
+ info.mBucketEndNs = currentBucketEndTimeNs;
+ info.mBucketNum = mCurrentBucketNum;
info.mDuration = mDuration;
(*output)[mEventKey].push_back(info);
- addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
VLOG(" final duration for last bucket: %lld", (long long)mDuration);
}
- mDuration = 0;
- if (hasOnGoingStartedEvent) {
- for (int i = 1; i < numBucketsForward; i++) {
- DurationBucket info;
- info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
- info.mBucketEndNs = endTime + mBucketSizeNs * i;
- info.mBucketNum = mCurrentBucketNum + i;
- info.mDuration = mBucketSizeNs;
- (*output)[mEventKey].push_back(info);
- addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
- VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs);
- }
+ if (numBucketsForward > 0) {
+ mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+ } else { // We must be forming a partial bucket.
+ mCurrentBucketStartTimeNs = eventTimeNs;
}
- mCurrentBucketNum += numBucketsForward;
+ mDuration = 0;
// If this tracker has no pending events, tell owner to remove.
return !hasPendingEvent;
}
+bool MaxDurationTracker::flushIfNeeded(
+ uint64_t eventTimeNs, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
+ if (eventTimeNs < getCurrentBucketEndTimeNs()) {
+ return false;
+ }
+ return flushCurrentBucket(eventTimeNs, output);
+}
+
void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& pair : mInfos) {
if (pair.second.state == kStopped) {
continue;
}
- bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
- ConditionState::kTrue;
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState = mWizard->query(
+ mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
+ &conditionDimensionKeySet);
+ bool conditionMet = (conditionState == ConditionState::kTrue) &&
+ (!mDimensionInCondition.has_field() ||
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+ conditionDimensionKeySet.end());
VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
noteConditionChanged(pair.first, conditionMet, timestamp);
}
@@ -251,7 +246,6 @@
void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
const uint64_t timestamp) {
- declareAnomalyIfAlarmExpired(timestamp);
auto it = mInfos.find(key);
if (it == mInfos.end()) {
return;
@@ -281,7 +275,6 @@
}
if (it->second.lastDuration > mDuration) {
mDuration = it->second.lastDuration;
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
}
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 661d131..fba4119 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,11 +28,17 @@
// they stop or bucket expires.
class MaxDurationTracker : public DurationTracker {
public:
- MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t currentBucketNum,
+ uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+
+ MaxDurationTracker(const MaxDurationTracker& tracker) = default;
+
+ unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -41,7 +47,10 @@
bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
+ bool flushCurrentBucket(
+ const uint64_t& eventTimeNs,
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>*) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
@@ -63,7 +72,6 @@
FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
FRIEND_TEST(MaxDurationTrackerTest, TestStopAll);
- FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 75d7c08..85f7b7c 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -25,17 +25,26 @@
using std::pair;
OringDurationTracker::OringDurationTracker(
- const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex, const FieldMatcher& dimensionInCondition,
+ bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum,
+ uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- bucketSizeNs, conditionSliced, anomalyTrackers),
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+ currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
+ conditionSliced, anomalyTrackers),
mStarted(),
mPaused() {
mLastStartTime = 0;
}
+unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) {
+ auto clonedTracker = make_unique<OringDurationTracker>(*this);
+ clonedTracker->mLastStartTime = eventTime;
+ clonedTracker->mDuration = 0;
+ return clonedTracker;
+}
+
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
@@ -45,7 +54,7 @@
if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mConditionKeyMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+ mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -76,7 +85,6 @@
if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
mConditionKeyMap[key] = conditionKey;
}
-
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
}
@@ -93,7 +101,7 @@
}
if (mStarted.empty()) {
mDuration += (timestamp - mLastStartTime);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
(long long)mDuration);
}
@@ -118,7 +126,7 @@
mDuration += (timestamp - mLastStartTime);
VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
(long long)mDuration);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
}
stopAnomalyAlarm();
@@ -127,51 +135,83 @@
mConditionKeyMap.clear();
}
-bool OringDurationTracker::flushIfNeeded(
- uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
- if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
- return false;
- }
+bool OringDurationTracker::flushCurrentBucket(
+ const uint64_t& eventTimeNs,
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
VLOG("OringDurationTracker Flushing.............");
- // adjust the bucket start time
- int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- DurationBucket current_info;
- current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
- current_info.mBucketEndNs = current_info.mBucketStartNs + mBucketSizeNs;
- current_info.mBucketNum = mCurrentBucketNum;
+
+ // Note that we have to mimic the bucket time changes we do in the
+ // MetricProducer#notifyAppUpgrade.
+
+ int numBucketsForward = 0;
+ uint64_t fullBucketEnd = getCurrentBucketEndTimeNs();
+ uint64_t currentBucketEndTimeNs;
+
+ if (eventTimeNs >= fullBucketEnd) {
+ numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs;
+ currentBucketEndTimeNs = fullBucketEnd;
+ } else {
+ // This must be a partial bucket.
+ currentBucketEndTimeNs = eventTimeNs;
+ }
+
// Process the current bucket.
if (mStarted.size() > 0) {
- mDuration += (current_info.mBucketEndNs - mLastStartTime);
+ mDuration += (currentBucketEndTimeNs - mLastStartTime);
}
if (mDuration > 0) {
+ DurationBucket current_info;
+ current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
+ current_info.mBucketEndNs = currentBucketEndTimeNs;
+ current_info.mBucketNum = mCurrentBucketNum;
current_info.mDuration = mDuration;
(*output)[mEventKey].push_back(current_info);
- addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum);
+ mDurationFullBucket += mDuration;
+ if (eventTimeNs > fullBucketEnd) {
+ // End of full bucket, can send to anomaly tracker now.
+ addPastBucketToAnomalyTrackers(mDurationFullBucket, current_info.mBucketNum);
+ mDurationFullBucket = 0;
+ }
VLOG(" duration: %lld", (long long)current_info.mDuration);
}
if (mStarted.size() > 0) {
for (int i = 1; i < numBucketsForward; i++) {
DurationBucket info;
- info.mBucketStartNs = mCurrentBucketStartTimeNs + mBucketSizeNs * i;
+ info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1);
info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
info.mBucketNum = mCurrentBucketNum + i;
info.mDuration = mBucketSizeNs;
(*output)[mEventKey].push_back(info);
+ // Safe to send these buckets to anomaly tracker since they must be full buckets.
+ // If it's a partial bucket, numBucketsForward would be 0.
addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
}
}
- mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
- mCurrentBucketNum += numBucketsForward;
- mLastStartTime = mCurrentBucketStartTimeNs;
mDuration = 0;
+ if (numBucketsForward > 0) {
+ mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+ } else { // We must be forming a partial bucket.
+ mCurrentBucketStartTimeNs = eventTimeNs;
+ }
+ mLastStartTime = mCurrentBucketStartTimeNs;
+
// if all stopped, then tell owner it's safe to remove this tracker.
return mStarted.empty() && mPaused.empty();
}
+bool OringDurationTracker::flushIfNeeded(
+ uint64_t eventTimeNs, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
+ if (eventTimeNs < getCurrentBucketEndTimeNs()) {
+ return false;
+ }
+ return flushCurrentBucket(eventTimeNs, output);
+}
+
void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
declareAnomalyIfAlarmExpired(timestamp);
vector<pair<HashableDimensionKey, int>> startedToPaused;
@@ -184,8 +224,14 @@
++it;
continue;
}
- if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
- ConditionState::kTrue) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState =
+ mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+ mDimensionInCondition, &conditionDimensionKeySet);
+ if (conditionState != ConditionState::kTrue ||
+ (mDimensionInCondition.has_field() &&
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
+ conditionDimensionKeySet.end())) {
startedToPaused.push_back(*it);
it = mStarted.erase(it);
VLOG("Key %s started -> paused", key.c_str());
@@ -198,7 +244,7 @@
mDuration += (timestamp - mLastStartTime);
VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
(long long)mDuration);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
}
}
@@ -210,8 +256,14 @@
++it;
continue;
}
- if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
- ConditionState::kTrue) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState =
+ mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+ mDimensionInCondition, &conditionDimensionKeySet);
+ if (conditionState == ConditionState::kTrue &&
+ (!mDimensionInCondition.has_field() ||
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition())
+ != conditionDimensionKeySet.end())) {
pausedToStarted.push_back(*it);
it = mPaused.erase(it);
VLOG("Key %s paused -> started", key.c_str());
@@ -256,7 +308,7 @@
mDuration += (timestamp - mLastStartTime);
mPaused.insert(mStarted.begin(), mStarted.end());
mStarted.clear();
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
}
}
if (mStarted.empty()) {
@@ -279,7 +331,7 @@
// As we move into the future, old buckets get overwritten (so their old data is erased).
// Sum of past durations. Will change as we overwrite old buckets.
- int64_t pastNs = mDuration;
+ int64_t pastNs = mDuration + mDurationFullBucket;
pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey);
// How much of the threshold is still unaccounted after considering pastNs.
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 43469ca..73e50e0 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,11 +28,16 @@
class OringDurationTracker : public DurationTracker {
public:
OringDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t currentBucketNum,
+ uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+ OringDurationTracker(const OringDurationTracker& tracker) = default;
+
+ unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -42,9 +47,12 @@
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
+ bool flushCurrentBucket(
+ const uint64_t& eventTimeNs,
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
const uint64_t currentTimestamp) const override;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index a0173ee..205c8e4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "../condition/CombinationConditionTracker.h"
@@ -541,7 +541,7 @@
ALOGE("initLogMatchingTrackers failed");
return false;
}
- ALOGD("initLogMatchingTrackers succeed...");
+ VLOG("initLogMatchingTrackers succeed...");
if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
trackerToConditionMap)) {
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
index df29eb0..03cb364 100644
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ b/cmds/statsd/src/packages/PackageInfoListener.h
@@ -28,13 +28,15 @@
public:
// Uid map will notify this listener that the app with apk name and uid has been upgraded to
// the specified version.
- virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int64_t version) = 0;
+ virtual void notifyAppUpgrade(const uint64_t& eventTimeNs, const std::string& apk,
+ const int uid, const int64_t version) = 0;
// Notify interested listeners that the given apk and uid combination no longer exits.
- virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0;
+ virtual void notifyAppRemoved(const uint64_t& eventTimeNs, const std::string& apk,
+ const int uid) = 0;
// Notify the listener that the UidMap snapshot is available.
- virtual void onUidMapReceived() = 0;
+ virtual void onUidMapReceived(const uint64_t& eventTimeNs) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 91279661..0d7b722 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "guardrail/StatsdStats.h"
@@ -119,7 +119,7 @@
for (auto weakPtr : broadcastList) {
auto strongPtr = weakPtr.promote();
if (strongPtr != NULL) {
- strongPtr->onUidMapReceived();
+ strongPtr->onUidMapReceived(timestamp);
}
}
}
@@ -166,7 +166,7 @@
for (auto weakPtr : broadcastList) {
auto strongPtr = weakPtr.promote();
if (strongPtr != NULL) {
- strongPtr->notifyAppUpgrade(appName, uid, versionCode);
+ strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
}
}
}
@@ -239,7 +239,7 @@
for (auto weakPtr : broadcastList) {
auto strongPtr = weakPtr.promote();
if (strongPtr != NULL) {
- strongPtr->notifyAppRemoved(app, uid);
+ strongPtr->notifyAppRemoved(timestamp, app, uid);
}
}
}
@@ -316,13 +316,13 @@
mLastUpdatePerConfigKey[key] = timestamp;
int64_t newMin = getMinimumTimestampNs();
- if (newMin > prevMin) {
+ if (newMin > prevMin) { // Delete anything possible now that the minimum has moved forward.
int64_t cutoff_nanos = newMin;
auto snapshots = mOutput.mutable_snapshots();
auto it_snapshots = snapshots->cbegin();
while (it_snapshots != snapshots->cend()) {
if (it_snapshots->timestamp_nanos() < cutoff_nanos) {
- // it_snapshots now points to the following element.
+ // it_snapshots points to the following element after erasing.
it_snapshots = snapshots->erase(it_snapshots);
} else {
++it_snapshots;
@@ -332,7 +332,7 @@
auto it_deltas = deltas->cbegin();
while (it_deltas != deltas->cend()) {
if (it_deltas->timestamp_nanos() < cutoff_nanos) {
- // it_deltas now points to the following element.
+ // it_snapshots points to the following element after erasing.
it_deltas = deltas->erase(it_deltas);
} else {
++it_deltas;
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 3304f6c..f1da452 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,7 +91,7 @@
void removeIsolatedUid(int isolatedUid, int parentUid);
// Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in.
- int getHostUidOrSelf(int uid) const;
+ virtual int getHostUidOrSelf(int uid) const;
// Gets the output. If every config key has received the output, then the output is cleared.
UidMapping getOutput(const ConfigKey& key);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index af21ca4..b56cffb 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -143,9 +143,7 @@
message StatsLogReport {
optional int64 metric_id = 1;
- optional int64 start_report_nanos = 2;
-
- optional int64 end_report_nanos = 3;
+ // Fields 2 and 3 are reserved.
message EventMetricDataWrapper {
repeated EventMetricData data = 1;
@@ -177,6 +175,10 @@
repeated StatsLogReport metrics = 1;
optional UidMapping uid_map = 2;
+
+ optional int64 last_report_nanos = 3;
+
+ optional int64 current_report_nanos = 4;
}
message ConfigMetricsReportList {
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a41f30c..6c61400 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -54,6 +54,9 @@
void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
ProtoOutputStream* protoOutput) {
+ if (!dimensionsValue.has_field()) {
+ return;
+ }
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
switch (dimensionsValue.value_case()) {
case DimensionsValue::ValueCase::kValueStr:
@@ -103,6 +106,9 @@
void writeFieldProtoToStream(
const Field& field, util::ProtoOutputStream* protoOutput) {
+ if (!field.has_field()) {
+ return;
+ }
protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
if (field.has_position_index()) {
protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 160b1f4..31f51a7 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -28,13 +28,14 @@
namespace statsd {
const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
+const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
// Minimum bucket size in seconds
const long kMinBucketSizeSec = 5 * 60;
typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
-typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
+typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 2ea79a6..5a326a4 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -64,7 +64,7 @@
oneof value_matcher {
bool eq_bool = 3;
string eq_string = 4;
- int32 eq_int = 5;
+ int64 eq_int = 5;
int64 lt_int = 6;
int64 gt_int = 7;
@@ -275,6 +275,12 @@
message IncidentdDetails {
repeated int32 section = 1;
+
+ enum Destination {
+ AUTOMATIC = 0;
+ EXPLICIT = 1;
+ }
+ optional Destination dest = 2;
}
message PerfettoDetails {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 00d8658..23bd5561 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "android-base/stringprintf.h"
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
new file mode 100644
index 0000000..d9a8fc8
--- /dev/null
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG true
+#include "Log.h"
+
+#include "IncidentdReporter.h"
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
+ const ConfigKey& configKey) {
+ if (config.section_size() == 0) {
+ VLOG("The alert %lld contains zero section in config(%d,%lld)", alert.id(),
+ configKey.GetUid(), (long long) configKey.GetId());
+ return false;
+ }
+
+ IncidentReportArgs incidentReport;
+
+ android::os::IncidentHeaderProto header;
+ header.set_alert_id(alert.id());
+ header.mutable_config_key()->set_uid(configKey.GetUid());
+ header.mutable_config_key()->set_id(configKey.GetId());
+ incidentReport.addHeader(header);
+
+ for (int i = 0; i < config.section_size(); i++) {
+ incidentReport.addSection(config.section(i));
+ }
+
+ uint8_t dest;
+ switch (config.dest()) {
+ case IncidentdDetails_Destination_AUTOMATIC:
+ dest = android::os::DEST_AUTOMATIC;
+ break;
+ case IncidentdDetails_Destination_EXPLICIT:
+ dest = android::os::DEST_EXPLICIT;
+ break;
+ default:
+ dest = android::os::DEST_AUTOMATIC;
+ }
+ incidentReport.setDest(dest);
+
+ sp<IIncidentManager> service = interface_cast<IIncidentManager>(
+ defaultServiceManager()->getService(android::String16("incident")));
+ if (service == nullptr) {
+ ALOGW("Failed to fetch incident service.");
+ return false;
+ }
+ VLOG("Calling incidentd %p", service.get());
+ binder::Status s = service->reportIncident(incidentReport);
+ VLOG("Report incident status: %s", s.toString8().string());
+ return s.isOk();
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
new file mode 100644
index 0000000..229ed77
--- /dev/null
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Calls incidentd to trigger an incident report and put in dropbox for uploading.
+ */
+bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
+ const ConfigKey& configKey);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index f912e4b..3af684f 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -56,7 +56,7 @@
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const {
+ const MetricDimensionKey& dimKey) const {
// Reminder about ids:
// subscription id - name of the Subscription (that ties the Alert to the broadcast)
// subscription rule_id - the name of the Alert (that triggers the broadcast)
@@ -92,7 +92,7 @@
void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const {
+ const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
if (mStatsCompanionService == nullptr) {
ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
@@ -107,8 +107,8 @@
}
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
- const HashableDimensionKey& dimKey) {
- return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
+ const MetricDimensionKey& dimKey) {
+ return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue());
}
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 5bb458a..13fc7fd 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -80,7 +80,7 @@
*/
void alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const;
+ const MetricDimensionKey& dimKey) const;
private:
SubscriberReporter() {};
@@ -101,7 +101,7 @@
void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const;
+ const MetricDimensionKey& dimKey) const;
/** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
static StatsDimensionsValue protoToStatsDimensionsValue(
@@ -109,7 +109,7 @@
/** Converts a HashableDimensionKey to a StatsDimensionsValue. */
static StatsDimensionsValue protoToStatsDimensionsValue(
- const HashableDimensionKey& dimKey);
+ const MetricDimensionKey& dimKey);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 66bfa68..a415ea1 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -33,14 +33,14 @@
const ConfigKey kConfigKey(0, 12345);
-HashableDimensionKey getMockDimensionKey(int key, string value) {
+MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
DimensionsValue dimensionsValue;
dimensionsValue.set_field(key);
dimensionsValue.set_value_str(value);
- return HashableDimensionKey(dimensionsValue);
+ return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
}
-void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
+void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
std::shared_ptr<DimToValMap> bucket) {
for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
(*bucket)[itr->first] += itr->second;
@@ -48,7 +48,7 @@
}
std::shared_ptr<DimToValMap> MockBucket(
- const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) {
+ const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
AddValueToBucket(key_value_pair_list, bucket);
return bucket;
@@ -56,7 +56,7 @@
// Returns the value, for the given key, in that bucket, or 0 if not present.
int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
- const HashableDimensionKey& key) {
+ const MetricDimensionKey& key) {
const auto& itr = bucket->find(key);
if (itr != bucket->end()) {
return itr->second;
@@ -68,14 +68,14 @@
bool detectAnomaliesPass(AnomalyTracker& tracker,
const int64_t& bucketNum,
const std::shared_ptr<DimToValMap>& currentBucket,
- const std::set<const HashableDimensionKey>& trueList,
- const std::set<const HashableDimensionKey>& falseList) {
- for (HashableDimensionKey key : trueList) {
+ const std::set<const MetricDimensionKey>& trueList,
+ const std::set<const MetricDimensionKey>& falseList) {
+ for (MetricDimensionKey key : trueList) {
if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
}
- for (HashableDimensionKey key : falseList) {
+ for (MetricDimensionKey key : falseList) {
if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
@@ -100,7 +100,7 @@
void checkRefractoryTimes(AnomalyTracker& tracker,
const int64_t& currTimestampNs,
const int32_t& refractoryPeriodSec,
- const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) {
+ const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
for (const auto& kv : timestamps) {
if (kv.second < 0) {
// Make sure that, if there is a refractory period, it is already past.
@@ -124,9 +124,9 @@
alert.set_trigger_if_sum_gt(2);
AnomalyTracker anomalyTracker(alert, kConfigKey);
- HashableDimensionKey keyA = getMockDimensionKey(1, "a");
- HashableDimensionKey keyB = getMockDimensionKey(1, "b");
- HashableDimensionKey keyC = getMockDimensionKey(1, "c");
+ MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+ MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+ MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
int64_t eventTimestamp0 = 10 * NS_PER_SEC;
int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
@@ -269,11 +269,11 @@
alert.set_trigger_if_sum_gt(2);
AnomalyTracker anomalyTracker(alert, kConfigKey);
- HashableDimensionKey keyA = getMockDimensionKey(1, "a");
- HashableDimensionKey keyB = getMockDimensionKey(1, "b");
- HashableDimensionKey keyC = getMockDimensionKey(1, "c");
- HashableDimensionKey keyD = getMockDimensionKey(1, "d");
- HashableDimensionKey keyE = getMockDimensionKey(1, "e");
+ MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+ MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+ MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
+ MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
+ MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 819f2be..d1b7b28 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -78,7 +78,7 @@
std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
const Position position,
const std::vector<int> &uids, const string& conditionName) {
- std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
+ std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
std::vector<int> uid_indexes;
switch(position) {
case Position::FIRST:
@@ -265,6 +265,9 @@
TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
for (Position position :
{ Position::ANY, Position::FIRST, Position::LAST}) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
+
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -307,7 +310,8 @@
const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
@@ -361,7 +365,8 @@
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
@@ -369,6 +374,9 @@
}
TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
+
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
Position::ANY /* position */);
@@ -410,7 +418,8 @@
ConditionKey queryKey;
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
@@ -452,13 +461,17 @@
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ dimensionKeys.clear();
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
TEST(SimpleConditionTrackerTest, TestStopAll) {
for (Position position :
{Position::ANY, Position::FIRST, Position::LAST}) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -502,7 +515,8 @@
const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by uid2
@@ -528,8 +542,9 @@
// TEST QUERY
const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -550,15 +565,15 @@
// TEST QUERY
const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
// TEST QUERY
const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp
new file mode 100644
index 0000000..678abae
--- /dev/null
+++ b/cmds/statsd/tests/dimension_test.cpp
@@ -0,0 +1,149 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dimension.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+
+TEST(DimensionTest, subLeafNodes) {
+ DimensionsValue dimension;
+ int tagId = 100;
+ dimension.set_field(tagId);
+ auto child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(1);
+ child->set_value_int(2000);
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(3);
+ child->set_value_str("test");
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(4);
+ auto grandChild = child->mutable_value_tuple()->add_dimensions_value();
+ grandChild->set_field(1);
+ grandChild->set_value_float(1.3f);
+ grandChild = child->mutable_value_tuple()->add_dimensions_value();
+ grandChild->set_field(3);
+ grandChild->set_value_str("tag");
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(6);
+ child->set_value_bool(false);
+
+ DimensionsValue sub_dimension;
+ FieldMatcher matcher;
+
+ // Tag id not matched.
+ matcher.set_field(tagId + 1);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field not exist.
+ matcher.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(5);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(6);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Not leaf field.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(4);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Grand-child leaf field not exist.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ auto childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(2);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Grand-child leaf field.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Multiple grand-child fields.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Multiple fields.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ matcher.add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Subset of the fields not exist.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ matcher.add_child()->set_field(2);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
new file mode 100644
index 0000000..b5d48ef
--- /dev/null
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -0,0 +1,725 @@
+// 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateCountMetricWithNoLinkConfig() {
+ StatsdConfig config;
+ auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by any attribution node and both by uid and tag.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidAndTagDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
+
+ auto metric = config.add_count_metric();
+ metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
+ metric->set_what(screenBrightnessChangeAtomMatcher.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ metric->set_bucket(ONE_MINUTE);
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
+ ConfigKey cfgKey;
+ auto config = CreateCountMetricWithNoLinkConfig();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10));
+
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 200));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
+
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50));
+
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 11));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 101));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 201));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + 203));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs - 99));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs - 2));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + bucketSizeNs - 1));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs + 2));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+ EXPECT_EQ(countMetrics.data_size(), 7);
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs );
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 3);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).count(), 1);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+ data = countMetrics.data(5);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(6);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+}
+
+namespace {
+
+StatsdConfig CreateCountMetricWithLinkConfig() {
+ StatsdConfig config;
+ auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+ *config.add_atom_matcher() = appCrashMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field*/);
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_count_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("AppCrashMetric"));
+ metric->set_what(appCrashMatcher.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = metric->add_links();
+ links->set_condition(isSyncingPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
+ ConfigKey cfgKey;
+ auto config = CreateCountMetricWithLinkConfig();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
+ events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
+
+ events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
+ events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
+
+ events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700));
+
+ events.push_back(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 + 400));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 600));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+ EXPECT_EQ(countMetrics.data_size(), 5);
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).count(), 1);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field */);
+
+ *config.add_predicate() = inBatterySaverModePredicate;
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_duration_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("BatterySaverModeDurationMetric"));
+ metric->set_what(inBatterySaverModePredicate.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+ ConfigKey cfgKey;
+ auto config = CreateDurationMetricConfigNoLink(aggregationType);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800));
+
+ events.push_back(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;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ 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);
+
+ EXPECT_EQ(metrics.data_size(), 3);
+ 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_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ 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);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ 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);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ }
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field */);
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ *config.add_predicate() = isInBackgroundPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_duration_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("AppInBackgroundMetric"));
+ metric->set_what(isInBackgroundPredicate.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = metric->add_links();
+ links->set_condition(isSyncingPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+ ConfigKey cfgKey;
+ auto config = CreateDurationMetricConfigWithLink(aggregationType);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
+ events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
+
+ events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
+ events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
+
+ events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
+ events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801));
+
+ events.push_back(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;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ 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);
+
+ EXPECT_EQ(metrics.data_size(), 3);
+ auto data = metrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = metrics.data(1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ 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_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 4504a95..233031c 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -44,9 +44,10 @@
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
auto isSyncingPredicate = CreateIsSyncingPredicate();
- *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(
- android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/});
+ 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 isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
@@ -78,9 +79,8 @@
auto dimensionWhat = links->mutable_fields_in_what();
dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
- auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
- dimensionCondition->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
// Links between crash atom and condition of app is in background.
links = countMetric->add_links();
@@ -88,7 +88,7 @@
dimensionWhat = links->mutable_fields_in_what();
dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
- dimensionCondition = links->mutable_fields_in_condition();
+ auto dimensionCondition = links->mutable_fields_in_condition();
dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
dimensionCondition->add_child()->set_field(1); // uid field.
return config;
@@ -132,12 +132,14 @@
CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ std::vector<AttributionNode> attributions =
+ {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
auto syncOnEvent1 =
- CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+ CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
auto syncOffEvent1 =
- CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+ CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
auto syncOnEvent2 =
- CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+ CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
auto moveToBackgroundEvent1 =
CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 1186a16..a99dbe8 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -40,9 +40,11 @@
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
// The predicate is dimensioning by any attribution node and both by uid and tag.
- *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateAttributionUidAndTagDimensions(
+ FieldMatcher dimensions = CreateAttributionUidAndTagDimensions(
android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST});
+ // Also slice by the wakelock tag
+ dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
*config.add_predicate() = holdingWakelockPredicate;
auto durationMetric = config.add_duration_metric();
@@ -58,133 +60,198 @@
return config;
}
-} // namespace
+std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
+ CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
- ConfigKey cfgKey;
- for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) {
- auto config = CreateStatsdConfig(aggregationType);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
+std::vector<AttributionNode> attributions2 = {CreateAttribution(111, "App2"),
+ CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+/*
+Events:
+Screen off is met from (200ns,1 min+500ns].
+Acquire event for wl1 from 2ns to 1min+2ns
+Acquire event for wl2 from 1min-10ns to 2min-15ns
+*/
+void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) {
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- auto screenTurnedOnEvent =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + 1);
- auto screenTurnedOffEvent =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 200);
- auto screenTurnedOnEvent2 =
+ auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
+ auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200);
+ auto screenTurnedOnEvent2 =
CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
bucketStartTimeNs + bucketSizeNs + 500);
- std::vector<AttributionNode> attributions1 =
- {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
+ auto acquireEvent1 = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+ auto releaseEvent1 =
+ CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
+ auto acquireEvent2 =
+ CreateAcquireWakelockEvent(attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10);
+ auto releaseEvent2 = CreateReleaseWakelockEvent(attributions2, "wl2",
+ bucketStartTimeNs + 2 * bucketSizeNs - 15);
- std::vector<AttributionNode> attributions2 =
- {CreateAttribution(111, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
+ std::vector<std::unique_ptr<LogEvent>> events;
- auto acquireEvent1 = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 2);
- auto acquireEvent2 = CreateAcquireWakelockEvent(
- attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10);
+ events.push_back(std::move(screenTurnedOnEvent));
+ events.push_back(std::move(screenTurnedOffEvent));
+ events.push_back(std::move(screenTurnedOnEvent2));
+ events.push_back(std::move(acquireEvent1));
+ events.push_back(std::move(acquireEvent2));
+ events.push_back(std::move(releaseEvent1));
+ events.push_back(std::move(releaseEvent2));
- auto releaseEvent1 = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
- auto releaseEvent2 = CreateReleaseWakelockEvent(
- attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15);
+ sortLogEventsByTimestamp(&events);
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(std::move(screenTurnedOnEvent));
- events.push_back(std::move(screenTurnedOffEvent));
- events.push_back(std::move(screenTurnedOnEvent2));
- events.push_back(std::move(acquireEvent1));
- events.push_back(std::move(acquireEvent2));
- events.push_back(std::move(releaseEvent1));
- events.push_back(std::move(releaseEvent2));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(
- data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
- // Validate bucket info.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
- data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // The wakelock holding interval starts from the screen off event and to the end of the 1st
- // bucket.
- EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
-
- reports.Clear();
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- // Dump the report after the end of 2nd bucket.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
- data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111);
- // Two output buckets.
- // The wakelock holding interval in the 1st bucket starts from the screen off event and to
- // the end of the 1st bucket.
- EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
- bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
- // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
- // ends at the second screen on event.
- EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
-
- events.clear();
- events.push_back(CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 2 * bucketSizeNs + 90));
- events.push_back(CreateAcquireWakelockEvent(
- attributions1, "wl3", bucketStartTimeNs + 2 * bucketSizeNs + 100));
- events.push_back(CreateReleaseWakelockEvent(
- attributions1, "wl3", bucketStartTimeNs + 5 * bucketSizeNs + 100));
- sortLogEventsByTimestamp(&events);
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- reports.Clear();
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
- data = reports.reports(0).metrics(0).duration_metrics().data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111);
- // The last wakelock holding spans 4 buckets.
- EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
- EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
- EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
- EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
}
}
+} // namespace
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) {
+ ConfigKey cfgKey;
+ auto config = CreateStatsdConfig(DurationMetric::SUM);
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ FeedEvents(config, processor);
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+
+ auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, 111);
+ // Validate bucket info.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // The wakelock holding interval starts from the screen off event and to the end of the 1st
+ // bucket.
+ EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
+
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ // Dump the report after the end of 2nd bucket.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, 111);
+ // Two output buckets.
+ // The wakelock holding interval in the 1st bucket starts from the screen off event and to
+ // the end of the 1st bucket.
+ EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
+ bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
+ // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
+ // ends at the second screen on event.
+ EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(
+ CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ bucketStartTimeNs + 2 * bucketSizeNs + 90));
+ events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3",
+ bucketStartTimeNs + 2 * bucketSizeNs + 100));
+ events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3",
+ bucketStartTimeNs + 5 * bucketSizeNs + 100));
+ sortLogEventsByTimestamp(&events);
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, 111);
+ // The last wakelock holding spans 4 buckets.
+ EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
+ EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration) {
+ ConfigKey cfgKey;
+ auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ FeedEvents(config, processor);
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ // Nothing has ended in the first bucket.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 0);
+
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ // Dump the report after the end of 2nd bucket. One dimension with one bucket.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+ auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, 111);
+ // The max is acquire event for wl1 to screen off start.
+ EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(
+ CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ bucketStartTimeNs + 2 * bucketSizeNs + 90));
+ events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3",
+ bucketStartTimeNs + 2 * bucketSizeNs + 100));
+ events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3",
+ bucketStartTimeNs + 5 * bucketSizeNs + 100));
+ sortLogEventsByTimestamp(&events);
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, 111);
+ // The last wakelock holding spans 4 buckets.
+ EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_nanos(),
+ bucketStartTimeNs + 5 * bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_nanos(),
+ bucketStartTimeNs + 6 * bucketSizeNs);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
new file mode 100644
index 0000000..7d9c8a8
--- /dev/null
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -0,0 +1,269 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "external/puller_util.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+/*
+ * Test merge isolated and host uid
+ */
+
+int uidAtomTagId = android::util::CPU_TIME_PER_UID_FREQ;
+int nonUidAtomTagId = android::util::SYSTEM_UPTIME;
+int timestamp = 1234;
+int isolatedUid = 30;
+int isolatedAdditiveData = 31;
+int isolatedNonAdditiveData = 32;
+int hostUid = 20;
+int hostAdditiveData = 21;
+int hostNonAdditiveData = 22;
+
+void extractIntoVector(vector<shared_ptr<LogEvent>> events,
+ vector<vector<int>>& ret) {
+ ret.clear();
+ status_t err;
+ for (const auto& event : events) {
+ vector<int> vec;
+ vec.push_back(event->GetInt(1, &err));
+ vec.push_back(event->GetInt(2, &err));
+ vec.push_back(event->GetInt(3, &err));
+ ret.push_back(vec);
+ }
+}
+
+TEST(puller_util, MergeNoDimension) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->22->31
+ event->write(isolatedUid);
+ event->write(hostNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 22, 52};
+ EXPECT_EQ(1, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, MergeWithDimension) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(isolatedUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 22, 21};
+ vector<int> expectedV2 = {20, 32, 52};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, NoMergeHostUidOnly) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 20->32->31
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ // 20->32->31
+ // 20->22->21
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 31};
+ vector<int> expectedV2 = {20, 22, 21};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, IsolatedUidOnly) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 30->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ // 20->32->31
+ // 20->22->21
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 31};
+ vector<int> expectedV2 = {20, 22, 21};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(isolatedUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 31->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(isolatedUid + 1);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 73};
+ EXPECT_EQ(1, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, NoNeedToMerge) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event =
+ make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+ // 32
+ event->write(isolatedNonAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ event = make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+ // 22
+ event->write(hostNonAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+
+ EXPECT_EQ(2, (int)inputData.size());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 4ad2097..87a1079 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -67,9 +67,9 @@
// Flushes.
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -80,10 +80,10 @@
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
- const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
+ EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo2.mCount);
@@ -91,9 +91,9 @@
// nothing happens in bucket 3. we should not record anything for bucket 3.
countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets3.size());
}
@@ -124,10 +124,10 @@
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
{
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -167,9 +167,9 @@
{getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- 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);
@@ -181,9 +181,9 @@
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -191,6 +191,117 @@
EXPECT_EQ(1LL, bucketInfo.mCount);
}
+TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+
+ int tagId = 1;
+ int conditionTagId = 2;
+
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ Alert alert;
+ alert.set_num_buckets(3);
+ alert.set_trigger_if_sum_gt(2);
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
+ event1.write("111"); // uid
+ event1.init();
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
+ bucketStartTimeNs);
+ sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
+ EXPECT_TRUE(anomalyTracker != nullptr);
+
+ // Bucket is flushed yet.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+ // App upgrade forces bucket flush.
+ // Check that there's a past bucket and the bucket end is not adjusted.
+ countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((long long)bucketStartTimeNs,
+ countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ((long long)eventUpgradeTimeNs,
+ countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
+ EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ // Anomaly tracker only contains full buckets.
+ EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+ uint64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
+ // Next event occurs in same bucket as partial bucket created.
+ LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10);
+ event2.write("222"); // uid
+ event2.init();
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+ EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+ // Third event in following bucket.
+ LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10);
+ event3.write("333"); // uid
+ event3.init();
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+}
+
+TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+
+ int tagId = 1;
+ int conditionTagId = 2;
+
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
+ event1.write("111"); // uid
+ event1.init();
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
+ bucketStartTimeNs);
+
+ // Bucket is flushed yet.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+ // App upgrade forces bucket flush.
+ // Check that there's a past bucket and the bucket end is not adjusted.
+ countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((int64_t)bucketStartTimeNs,
+ countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+ (uint64_t)countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
+ EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+
+ // Next event occurs in same bucket as partial bucket created.
+ LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10);
+ event2.write("222"); // uid
+ event2.init();
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+ EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+
+ // Third event in following bucket.
+ LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10);
+ event3.write("333"); // uid
+ event3.init();
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((int64_t)eventUpgradeTimeNs,
+ countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ (uint64_t)countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
+}
+
TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
Alert alert;
alert.set_id(11);
@@ -229,13 +340,13 @@
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Two events in bucket #3.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
@@ -244,13 +355,13 @@
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
// Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
}
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index a59f1fe..23e15f7 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -62,9 +62,9 @@
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
- const auto& buckets = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -107,15 +107,240 @@
durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
- const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets2.size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
EXPECT_EQ(1ULL, buckets2[0].mDuration);
}
+TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
+ /**
+ * The duration starts from the first bucket, through the two partial buckets (10-70sec),
+ * another bucket, and ends at the beginning of the next full bucket.
+ * Expected buckets:
+ * - [10,25]: 14 secs
+ * - [25,70]: All 45 secs
+ * - [70,130]: All 60 secs
+ * - [130, 210]: Only 5 secs (event ended at 135sec)
+ */
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ uint64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
+ uint64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+
+ int tagId = 1;
+
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs));
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ std::vector<DurationBucket> buckets =
+ durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+ EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration);
+ EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs));
+ buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(3UL, buckets.size());
+ EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
+ EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
+ /**
+ * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
+ * - [10,70]: 59 secs
+ * - [70,75]: 5 sec
+ * - [75,130]: 55 secs
+ */
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+ uint64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
+ uint64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+
+ int tagId = 1;
+
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs));
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ std::vector<DurationBucket> buckets =
+ durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
+ EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs);
+ EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
+ EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs));
+ buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(3UL, buckets.size());
+ EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ uint64_t startTimeNs = bucketStartTimeNs + 1;
+ uint64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
+
+ int tagId = 1;
+
+ // Setup metric with alert.
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+ Alert alert;
+ alert.set_num_buckets(3);
+ alert.set_trigger_if_sum_gt(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert);
+ EXPECT_TRUE(anomalyTracker != nullptr);
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs));
+ durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs));
+
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
+ (uint64_t)anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+}
+
+TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ uint64_t startTimeNs = bucketStartTimeNs + 1;
+ uint64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+
+ int tagId = 1;
+
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
+ LogEvent event1(tagId, startTimeNs);
+ event1.write("111"); // uid
+ event1.init();
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs));
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs));
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+
+ durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ std::vector<DurationBucket> buckets =
+ durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(1UL, buckets.size());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+ uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+ uint64_t startTimeNs = bucketStartTimeNs + 1;
+ uint64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
+
+ int tagId = 1;
+
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
+ LogEvent event1(tagId, startTimeNs);
+ event1.write("111"); // uid
+ event1.init();
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs));
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ // Stop occurs in the same partial bucket as created for the app upgrade.
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs));
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+ durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ std::vector<DurationBucket> buckets =
+ durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(1UL, buckets.size());
+ EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index da00cae..3deab37 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -114,9 +114,9 @@
key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- 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 4533ac6..470d4d0 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -44,6 +44,7 @@
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
+const uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
TEST(GaugeMetricProducerTest, TestNoCondition) {
GaugeMetric metric;
@@ -119,6 +120,143 @@
EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
}
+TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
+ GaugeMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_gauge_fields_filter()->set_include_all(true);
+
+ Alert alert;
+ alert.set_id(101);
+ alert.set_metric_id(metricId);
+ alert.set_trigger_if_sum_gt(25);
+ alert.set_num_buckets(100);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1 /* -1 means no pulling */, bucketStartTimeNs,
+ pullerManager);
+ sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
+ EXPECT_TRUE(anomalyTracker != nullptr);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
+
+ gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ // Partial buckets are not sent to anomaly tracker.
+ EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+ // Create an event in the same partial bucket.
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
+ event2->write(1);
+ event2->write(10);
+ event2->init();
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ // Partial buckets are not sent to anomaly tracker.
+ EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+ // Next event should trigger creation of new bucket and send previous full bucket to anomaly
+ // tracker.
+ shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
+ event3->write(1);
+ event3->write(10);
+ event3->init();
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+ // Next event should trigger creation of new bucket.
+ shared_ptr<LogEvent> event4 =
+ make_shared<LogEvent>(tagId, bucketStartTimeNs + 125 * NS_PER_SEC);
+ event4->write(1);
+ event4->write(10);
+ event4->init();
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+ EXPECT_EQ(2UL, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+}
+
+TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
+ GaugeMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(tagId);
+ gaugeFieldMatcher->add_child()->set_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
+ event->write("some value");
+ event->write(2);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ tagId, bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write("some value");
+ event->write(1);
+ event->init();
+ allData.push_back(event);
+ gaugeProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
+ ->second.front()
+ .mFields->begin()
+ ->second.value_int());
+
+ gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ((uint64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
+ ->second.front()
+ .mFields->begin()
+ ->second.value_int());
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1);
+ event->write("some value");
+ event->write(3);
+ event->init();
+ allData.push_back(event);
+ gaugeProducer.onDataPulled(allData);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
+ ->second.front()
+ .mFields->begin()
+ ->second.value_int());
+}
+
TEST(GaugeMetricProducerTest, TestWithCondition) {
GaugeMetric metric;
metric.set_id(metricId);
@@ -218,7 +356,7 @@
EXPECT_EQ(13L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
std::shared_ptr<LogEvent> event2 =
std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
@@ -231,7 +369,7 @@
EXPECT_EQ(15L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
std::shared_ptr<LogEvent> event3 =
@@ -245,7 +383,7 @@
EXPECT_EQ(26L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
// The event4 does not have the gauge field. Thus the current bucket value is 0.
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 0772b0d40..2658e4e 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,18 +45,27 @@
const std::vector<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<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ false, {});
tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
// Event starts again. This would not change anything as it already starts.
@@ -75,16 +84,25 @@
}
TEST(MaxDurationTrackerTest, TestStopAll) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ false, {});
tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
@@ -93,58 +111,77 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets);
tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
EXPECT_TRUE(tracker.mInfos.empty());
-
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+ EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets);
- EXPECT_EQ(2u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(40ULL, buckets[eventKey][1].mDuration);
+ EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+ EXPECT_EQ(1u, buckets[eventKey].size());
+ EXPECT_EQ(bucketSizeNs + 40 - 1, buckets[eventKey][0].mDuration);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[eventKey][0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs);
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ false, {});
// The event starts.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
- // Starts again. Does not change anything.
+ // Starts again. Does not DEFAULT_DIMENSION_KEY anything.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
ConditionKey());
// The event stops at early 4th bucket.
+ // Notestop is called from DurationMetricProducer's onMatchedLogEvent, which calls
+ // flushIfneeded.
tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets);
tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (3 * bucketSizeNs) + 20,
false /*stop all*/);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(3u, buckets[eventKey].size());
- EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration);
- EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][1].mDuration);
- EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][2].mDuration);
+ EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
+
+ tracker.flushIfNeeded(bucketStartTimeNs + 4 * bucketSizeNs, &buckets);
+ EXPECT_EQ(1u, buckets[eventKey].size());
+ EXPECT_EQ((3 * bucketSizeNs) + 20 - 1, buckets[eventKey][0].mDuration);
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[eventKey][0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs);
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ false, {});
// 2 starts
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -153,42 +190,45 @@
tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/);
tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets);
-
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(2u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
+ // Because of nesting, still not stopped.
+ EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
// real stop now.
- tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+ tracker.noteStop(DEFAULT_DIMENSION_KEY,
+ bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
- EXPECT_EQ(3u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
- EXPECT_EQ(5ULL, buckets[eventKey][2].mDuration);
+ EXPECT_EQ(1u, buckets[eventKey].size());
+ EXPECT_EQ(2 * bucketSizeNs + 5 - 1, buckets[eventKey][0].mDuration);
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
- HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
+ MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
- EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4
+ EXPECT_CALL(*wizard, query(_, conditionKey1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
int64_t durationTimeNs = 2 * 1000;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- bucketSizeNs, true, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ true, {});
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
@@ -203,41 +243,6 @@
EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration);
}
-TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 1;
- alert.set_refractory_period_secs(refPeriodSec);
-
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
- uint64_t bucketSizeNs = 30 * NS_PER_SEC;
-
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketSizeNs, false, {anomalyTracker});
-
- tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
- tracker.noteStop(key1, eventStartTimeNs + 10, false);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
- EXPECT_EQ(10LL, tracker.mDuration);
-
- tracker.noteStart(key2, true, eventStartTimeNs + 20, ConditionKey());
- tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets);
- tracker.noteStop(key2, eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
- EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
-
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey),
- (eventStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + refPeriodSec);
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6b8893e..4b579b1 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -43,19 +43,30 @@
const std::vector<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;
TEST(OringDurationTrackerTest, TestDurationOverlap) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -71,15 +82,25 @@
}
TEST(OringDurationTrackerTest, TestDurationNested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
@@ -95,15 +116,25 @@
}
TEST(OringDurationTrackerTest, TestStopAll) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
@@ -118,16 +149,26 @@
}
TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
@@ -150,23 +191,33 @@
}
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
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<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -181,25 +232,35 @@
}
TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
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));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
// condition to false; record duration 5n
@@ -216,21 +277,31 @@
}
TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
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<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
- uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -249,6 +320,13 @@
}
TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -256,15 +334,17 @@
alert.set_num_buckets(2);
alert.set_refractory_period_secs(1);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
- uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {anomalyTracker});
// Nothing in the past bucket.
@@ -310,6 +390,12 @@
}
TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -318,16 +404,18 @@
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
+ uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
- uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
@@ -352,6 +440,13 @@
}
TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -360,7 +455,7 @@
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conkey;
conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
@@ -369,8 +464,9 @@
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
+ bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index fff3dbf..325a372 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -44,6 +44,7 @@
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
+const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
/*
* Tests pulled atoms with no conditions
@@ -202,6 +203,101 @@
EXPECT_EQ(false, curInterval.startUpdated);
}
+TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+ pullerManager);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
+ event2->write(1);
+ event2->write(10);
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+
+ // Next value should create a new bucket.
+ shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
+ event3->write(1);
+ event3->write(10);
+ event3->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
+}
+
+TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
+ pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ allData.push_back(event);
+
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(tagId);
+ event->write(150);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((uint64_t)bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue);
+}
+
TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
ValueMetric metric;
metric.set_id(metricId);
@@ -299,26 +395,26 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
// Value sum == 30 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
// Value sum == 130 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Three events in bucket #3.
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
// Anomaly at event 4 since Value sum == 131 > 130!
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
// Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
// Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
}
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index fc7245c..ab9345a 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -26,6 +26,13 @@
return HashableDimensionKey(dimensionsValue);
}
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_field(tagId);
+ dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
+ dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
+ return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+}
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 23e86f9..b48de54 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -15,6 +15,7 @@
#include "src/condition/ConditionWizard.h"
#include "src/external/StatsPullerManager.h"
+#include "src/packages/UidMap.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -25,10 +26,12 @@
class MockConditionWizard : public ConditionWizard {
public:
- MOCK_METHOD2(
+ MOCK_METHOD4(
query,
ConditionState(const int conditionIndex,
- const ConditionKey& conditionParameters));
+ const ConditionKey& conditionParameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet));
};
class MockStatsPullerManager : public StatsPullerManager {
@@ -38,7 +41,13 @@
MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
};
+class MockUidMap : public UidMap {
+ public:
+ MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid));
+};
+
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 9f4582d..13055cb 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <gtest/gtest.h>
#include "statsd_test_util.h"
namespace android {
@@ -27,6 +26,22 @@
return atom_matcher;
}
+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;
@@ -47,6 +62,30 @@
return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
}
+AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
+ const string& name, BatterySaverModeStateChanged::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::BATTERY_SAVER_MODE_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 CreateBatterySaverModeStartAtomMatcher() {
+ return CreateBatterySaverModeStateChangedAtomMatcher(
+ "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
+}
+
+
+AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
+ return CreateBatterySaverModeStateChangedAtomMatcher(
+ "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
+}
+
+
AtomMatcher CreateScreenStateChangedAtomMatcher(
const string& name, android::view::DisplayStateEnum state) {
AtomMatcher atom_matcher;
@@ -59,6 +98,7 @@
return atom_matcher;
}
+
AtomMatcher CreateScreenTurnedOnAtomMatcher() {
return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
@@ -128,6 +168,13 @@
"ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
}
+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;
@@ -218,6 +265,31 @@
return event;
}
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+ int level, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(level));
+ event->init();
+ return event;
+
+}
+
std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
const std::vector<AttributionNode>& attributions, const string& wakelockName,
const WakelockStateChanged::State state, uint64_t timestampNs) {
@@ -267,9 +339,10 @@
}
std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
- const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+ const std::vector<AttributionNode>& attributions,
+ const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
- event->write(uid);
+ event->write(attributions);
event->write(name);
event->write(state);
event->init();
@@ -277,13 +350,13 @@
}
std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const int uid, const string& name, uint64_t timestampNs){
- return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){
+ return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
}
std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const int uid, const string& name, uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) {
+ return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
}
std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index ff8fef0c..6638893 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,6 +14,7 @@
#pragma once
+#include <gtest/gtest.h>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "statslog.h"
#include "src/logd/LogEvent.h"
@@ -26,6 +27,18 @@
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
+// Create AtomMatcher proto for screen brightness state changed.
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
+
+// Create AtomMatcher proto for starting battery save mode.
+AtomMatcher CreateBatterySaverModeStartAtomMatcher();
+
+// Create AtomMatcher proto for stopping battery save mode.
+AtomMatcher CreateBatterySaverModeStopAtomMatcher();
+
+// Create AtomMatcher proto for process state changed.
+AtomMatcher CreateUidProcessStateChangedAtomMatcher();
+
// Create AtomMatcher proto for acquiring wakelock.
AtomMatcher CreateAcquireWakelockAtomMatcher();
@@ -59,6 +72,9 @@
// Create Predicate proto for screen is off.
Predicate CreateScreenIsOffPredicate();
+// Create Predicate proto for battery saver mode.
+Predicate CreateBatterySaverModePredicate();
+
// Create Predicate proto for holding wakelock.
Predicate CreateHoldingWakelockPredicate();
@@ -86,6 +102,15 @@
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
const android::view::DisplayStateEnum state, uint64_t timestampNs);
+// Create log event for screen brightness state changed.
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+ int level, uint64_t timestampNs);
+
+// Create log event when battery saver starts.
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
+// Create log event when battery saver stops.
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
+
// Create log event for app moving to background.
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
@@ -94,11 +119,11 @@
// Create log event when the app sync starts.
std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const int uid, const string& name, uint64_t timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const int uid, const string& name, uint64_t timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateAppCrashEvent(
@@ -136,9 +161,12 @@
template <typename T>
void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
- std::map<HashableDimensionKey, int> dimensionIndexMap;
+ std::map<MetricDimensionKey, int> dimensionIndexMap;
for (int i = 0; i < metricData.data_size(); ++i) {
- dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i));
+ dimensionIndexMap.insert(std::make_pair(
+ MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()),
+ HashableDimensionKey(metricData.data(i).dimensions_in_condition())),
+ i));
}
for (const auto& itr : dimensionIndexMap) {
*sortedMetricData->add_data() = metricData.data(itr.second);
diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml
index cd76c9d..52673fb 100644
--- a/cmds/statsd/tools/dogfood/AndroidManifest.xml
+++ b/cmds/statsd/tools/dogfood/AndroidManifest.xml
@@ -37,5 +37,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <service android:name=".MainActivity$ReceiverIntentService" android:exported="true" />
</application>
</manifest>
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
index 5d35c29..784ed40 100644
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
@@ -31,6 +31,18 @@
android:layout_height="wrap_content"
android:background="@android:color/holo_green_light"
android:text="@string/push_config"/>
+ <Button
+ android:id="@+id/set_receiver"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/holo_green_light"
+ android:text="@string/set_receiver"/>
+ <Button
+ android:id="@+id/remove_receiver"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/holo_green_light"
+ android:text="@string/remove_receiver"/>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml
index 0eab0f4..60948a1 100644
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ b/cmds/statsd/tools/dogfood/res/values/strings.xml
@@ -24,6 +24,8 @@
<string name="statsd_not_running">Statsd NOT Running</string>
<string name="push_config">Push baseline config</string>
+ <string name="set_receiver">Set pendingintent</string>
+ <string name="remove_receiver">Remove pendingintent</string>
<string name="app_a_foreground">App A foreground</string>
<string name="app_b_foreground">App B foreground</string>
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index 33c8abf..03e5fef 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -32,11 +32,13 @@
for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
+ sb.append("Last report time:").append(getDateStr(report.getLastReportNanos())).
+ append("\n");
+ sb.append("Current report time:").append(getDateStr(report.getCurrentReportNanos())).
+ append("\n");
for (StatsLog.StatsLogReport log : report.getMetricsList()) {
sb.append("\n\n");
sb.append("metric id: ").append(log.getMetricId()).append("\n");
- sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
- sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
switch (log.getDataCase()) {
case DURATION_METRICS:
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index d39aa1d..0e6c933 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -16,7 +16,10 @@
package com.android.statsd.dogfood;
import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.IntentService;
import android.app.StatsManager;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
@@ -48,96 +51,100 @@
findViewById(R.id.app_a_wake_lock_acquire1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(0, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(0, "wl_1");
+ }
+ });
findViewById(R.id.app_b_wake_lock_acquire1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(1, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(1, "wl_1");
+ }
+ });
findViewById(R.id.app_a_wake_lock_acquire2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(0, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(0, "wl_2");
+ }
+ });
findViewById(R.id.app_b_wake_lock_acquire2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(1, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(1, "wl_2");
+ }
+ });
findViewById(R.id.app_a_wake_lock_release1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(0, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(0, "wl_1");
+ }
+ });
findViewById(R.id.app_b_wake_lock_release1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(1, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(1, "wl_1");
+ }
+ });
findViewById(R.id.app_a_wake_lock_release2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(0, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(0, "wl_2");
+ }
+ });
findViewById(R.id.app_b_wake_lock_release2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(1, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(1, "wl_2");
+ }
+ });
findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
}
});
findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 0);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
}
});
findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 2);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
}
});
findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
}
});
@@ -187,8 +194,7 @@
byte[] config = new byte[inputStream.available()];
inputStream.read(config);
if (mStatsManager != null) {
- if (mStatsManager.addConfiguration(CONFIG_ID,
- config, getPackageName(), MainActivity.this.getClass().getName())) {
+ if (mStatsManager.addConfiguration(CONFIG_ID, config)) {
Toast.makeText(
MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
} else {
@@ -201,6 +207,55 @@
}
}
});
+
+ PendingIntent pi = PendingIntent.getService(this, 0,
+ new Intent(this, ReceiverIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
+ findViewById(R.id.set_receiver).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ try {
+ if (!statsdRunning()) {
+ return;
+ }
+ if (mStatsManager != null) {
+ if (mStatsManager.setDataFetchOperation(CONFIG_ID, pi)) {
+ Toast.makeText(MainActivity.this,
+ "Receiver specified to pending intent", Toast.LENGTH_LONG)
+ .show();
+ } else {
+ Toast.makeText(MainActivity.this, "Statsd did not set receiver",
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+ } catch (Exception e) {
+ Toast.makeText(MainActivity.this, "failed to set receiver", Toast.LENGTH_LONG);
+ }
+ }
+ });
+ findViewById(R.id.remove_receiver).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ try {
+ if (!statsdRunning()) {
+ return;
+ }
+ if (mStatsManager != null) {
+ if (mStatsManager.setDataFetchOperation(CONFIG_ID, null)) {
+ Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
+ .show();
+ } else {
+ Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+ } catch (Exception e) {
+ Toast.makeText(
+ MainActivity.this, "failed to remove receiver", Toast.LENGTH_LONG);
+ }
+ }
+ });
mStatsManager = (StatsManager) getSystemService("stats");
}
@@ -253,9 +308,11 @@
Log.d(TAG, "invalid pkg id");
return;
}
- int[] uids = new int[] {mUids[id]};
- String[] tags = new String[] {"acquire"};
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
+ int[] uids = new int[]{mUids[id]};
+ String[] tags = new String[]{"acquire"};
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 1);");
@@ -267,12 +324,30 @@
Log.d(TAG, "invalid pkg id");
return;
}
- int[] uids = new int[] {mUids[id]};
- String[] tags = new String[] {"release"};
- StatsLog.write(10, uids, tags, 0, name, 0);
+ int[] uids = new int[]{mUids[id]};
+ String[] tags = new String[]{"release"};
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 0);");
Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
}
+
+ public static class ReceiverIntentService extends IntentService {
+ public ReceiverIntentService() {
+ super("ReceiverIntentService");
+ }
+
+ /**
+ * The IntentService calls this method from the default worker thread with
+ * the intent that started the service. When this method returns, IntentService
+ * stops the service, as appropriate.
+ */
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ Log.i(TAG, "Received notification that we should call getData");
+ }
+ }
}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
index 75489ad..87b82c2 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
@@ -36,6 +36,10 @@
int numMetrics = 0;
for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
+ sb.append("Last report time:").append(getDateStr(report.getLastReportNanos())).
+ append("\n");
+ sb.append("Current report time:").append(getDateStr(report.getCurrentReportNanos())).
+ append("\n");
for (StatsLog.StatsLogReport log : report.getMetricsList()) {
numMetrics++;
if (numMetrics > MAX_NUM_METRICS_TO_DISPLAY) {
@@ -45,8 +49,6 @@
}
sb.append("\n");
sb.append("metric id: ").append(log.getMetricId()).append("\n");
- sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
- sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
switch (log.getDataCase()) {
case DURATION_METRICS:
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 652f6b2..bed4d98 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
@@ -47,10 +47,12 @@
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.StatsdStatsReport;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -60,18 +62,18 @@
* Runs a load test for statsd.
* How it works:
* <ul>
- * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
- * <li> Periodically logs certain atoms into logd.
- * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
- * in battery Historian.
+ * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
+ * <li> Periodically logs certain atoms into logd.
+ * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
+ * in battery Historian.
* </ul>
* The load depends on how demanding the config is, as well as how frequently atoms are pushsed
* to logd. Those are all controlled by 4 adjustable parameters:
* <ul>
- * <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
- * <li> The bucket size controls the time-bucketing the aggregate metrics.
- * <li> The period parameter controls how frequently atoms are pushed to logd.
- * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
+ * <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
+ * <li> The bucket size controls the time-bucketing the aggregate metrics.
+ * <li> The period parameter controls how frequently atoms are pushed to logd.
+ * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
* </ul>
*/
public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener {
@@ -93,7 +95,7 @@
Intent activityIntent = new Intent(context, LoadtestActivity.class);
activityIntent.putExtra(TYPE, PUSH_ALARM);
context.startActivity(activityIntent);
- }
+ }
}
public final static class StopperAlarmReceiver extends BroadcastReceiver {
@@ -102,7 +104,7 @@
Intent activityIntent = new Intent(context, LoadtestActivity.class);
activityIntent.putExtra(TYPE, STOP);
context.startActivity(activityIntent);
- }
+ }
}
private static Map<String, TimeUnit> initializeTimeUnitMap() {
@@ -119,6 +121,7 @@
labels.put("1s", TimeUnit.CTS);
return labels;
}
+
private static List<String> initializeTimeUnitLabels() {
List<String> labels = new ArrayList();
labels.add("1s");
@@ -136,10 +139,14 @@
private AlarmManager mAlarmMgr;
- /** Used to periodically log atoms to logd. */
+ /**
+ * Used to periodically log atoms to logd.
+ */
private PendingIntent mPushPendingIntent;
- /** Used to end the loadtest. */
+ /**
+ * Used to end the loadtest.
+ */
private PendingIntent mStopPendingIntent;
private Button mStartStop;
@@ -156,13 +163,19 @@
private CheckBox mValueMetricCheckBox;
private CheckBox mGaugeMetricCheckBox;
- /** When the load test started. */
+ /**
+ * When the load test started.
+ */
private long mStartedTimeMillis;
- /** For measuring perf data. */
+ /**
+ * For measuring perf data.
+ */
private PerfData mPerfData;
- /** For communicating with statsd. */
+ /**
+ * For communicating with statsd.
+ */
private StatsManager mStatsManager;
private PowerManager mPowerManager;
@@ -199,34 +212,54 @@
*/
private boolean mIncludeGaugeMetric;
- /** The burst size. */
+ /**
+ * The burst size.
+ */
private int mBurst;
- /** The metrics replication. */
+ /**
+ * The metrics replication.
+ */
private int mReplication;
- /** The period, in seconds, at which batches of atoms are pushed. */
+ /**
+ * The period, in seconds, at which batches of atoms are pushed.
+ */
private long mPeriodSecs;
- /** The bucket size, in minutes, for aggregate metrics. */
+ /**
+ * The bucket size, in minutes, for aggregate metrics.
+ */
private TimeUnit mBucket;
- /** The duration, in minutes, of the loadtest. */
+ /**
+ * The duration, in minutes, of the loadtest.
+ */
private long mDurationMins;
- /** Whether the loadtest has started. */
+ /**
+ * Whether the loadtest has started.
+ */
private boolean mStarted = false;
- /** Orchestrates the logging of pushed events into logd. */
+ /**
+ * Orchestrates the logging of pushed events into logd.
+ */
private SequencePusher mPusher;
- /** Generates statsd configs. */
+ /**
+ * Generates statsd configs.
+ */
private ConfigFactory mFactory;
- /** For intra-minute periods. */
+ /**
+ * For intra-minute periods.
+ */
private final Handler mHandler = new Handler();
- /** Number of metrics in the current config. */
+ /**
+ * Number of metrics in the current config.
+ */
private int mNumMetrics;
@Override
@@ -250,7 +283,7 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
InputMethodManager imm =
- (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null) {
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
@@ -380,10 +413,10 @@
}
// Piggy-back on that alarm to show the elapsed time.
long elapsedTimeMins = (long) Math.floor(
- (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+ (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
mReportText.setText("Loadtest in progress.\n"
- + "num metrics =" + mNumMetrics
- + "\nElapsed time = " + elapsedTimeMins + " min(s)");
+ + "num metrics =" + mNumMetrics
+ + "\nElapsed time = " + elapsedTimeMins + " min(s)");
}
private void onAlarm() {
@@ -402,12 +435,14 @@
mWakeLock = null;
}
- /** Schedules the next cycle of pushing atoms into logd. */
+ /**
+ * Schedules the next cycle of pushing atoms into logd.
+ */
private void scheduleNext() {
Intent intent = new Intent(this, PusherAlarmReceiver.class);
intent.putExtra(TYPE, PUSH_ALARM);
mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
- long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
+ long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
}
@@ -433,7 +468,7 @@
Intent intent = new Intent(this, StopperAlarmReceiver.class);
intent.putExtra(TYPE, STOP);
mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
- long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
+ long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent);
// Log atoms.
@@ -441,8 +476,8 @@
// Start tracking performance.
mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst,
- mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric,
- mIncludeGaugeMetric);
+ mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric,
+ mIncludeGaugeMetric);
mPerfData.startRecording(this);
mReportText.setText("Loadtest in progress.\nnum metrics =" + mNumMetrics);
@@ -476,7 +511,7 @@
getData();
long elapsedTimeMins = (long) Math.floor(
- (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+ (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
clearConfigs();
updateStarted(false);
@@ -485,7 +520,7 @@
private synchronized void updateStarted(boolean started) {
mStarted = started;
mStartStop.setBackgroundColor(started ?
- Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
+ Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start));
updateControlsEnabled();
}
@@ -538,22 +573,21 @@
}
private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
- if (mStatsManager != null) {
- if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID,
- configData.bytes, getPackageName(), LoadtestActivity.this.getClass().getName())) {
+ if (mStatsManager != null) {
+ if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID, configData.bytes)) {
mNumMetrics = configData.numMetrics;
Log.d(TAG, "Config pushed to statsd");
return true;
} else {
Log.d(TAG, "Failed to push config to statsd");
}
- }
- return false;
+ }
+ return false;
}
private synchronized void setReplication(int replication) {
if (mStarted) {
- return;
+ return;
}
mReplicationText.setText("" + replication);
}
@@ -614,13 +648,13 @@
mBucketSpinner = (Spinner) findViewById(R.id.bucket_spinner);
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(
- this, R.layout.spinner_item, TIME_UNIT_LABELS);
+ this, R.layout.spinner_item, TIME_UNIT_LABELS);
mBucketSpinner.setAdapter(dataAdapter);
mBucketSpinner.setOnItemSelectedListener(this);
for (String label : TIME_UNIT_MAP.keySet()) {
- if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
+ if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
mBucketSpinner.setSelection(dataAdapter.getPosition(label));
}
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
new file mode 100644
index 0000000..71de479
--- /dev/null
+++ b/config/hiddenapi-light-greylist.txt
@@ -0,0 +1,1449 @@
+Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+Landroid/animation/ValueAnimator;->animateValue(F)V
+Landroid/animation/ValueAnimator;->getDurationScale()F
+Landroid/animation/ValueAnimator;->sDurationScale:F
+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;->isLowRamDeviceStatic()Z
+Landroid/app/ActivityManager;->isUserRunning(I)Z
+Landroid/app/ActivityManager;->mContext:Landroid/content/Context;
+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;
+Landroid/app/Activity;->mHandler:Landroid/os/Handler;
+Landroid/app/Activity;->mResultCode:I
+Landroid/app/Activity;->mResultData:Landroid/content/Intent;
+Landroid/app/Activity;->mToken:Landroid/os/IBinder;
+Landroid/app/Activity;->mWindow:Landroid/view/Window;
+Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
+Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
+Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
+Landroid/app/ActivityThread$ActivityClientRecord;->token:Landroid/os/IBinder;
+Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
+Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle;
+Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread;
+Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application;
+Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String;
+Landroid/app/ActivityThread;->currentProcessName()Ljava/lang/String;
+Landroid/app/ActivityThread;->getApplication()Landroid/app/Application;
+Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread;
+Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler;
+Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager;
+Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String;
+Landroid/app/ActivityThread$H;->BIND_SERVICE:I
+Landroid/app/ActivityThread$H;->CREATE_SERVICE:I
+Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I
+Landroid/app/ActivityThread$H;->RECEIVER:I
+Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I
+Landroid/app/ActivityThread$H;->SERVICE_ARGS:I
+Landroid/app/ActivityThread$H;->STOP_SERVICE:I
+Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I
+Landroid/app/ActivityThread;->installContentProviders(Landroid/content/Context;Ljava/util/List;)V
+Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList;
+Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData;
+Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application;
+Landroid/app/ActivityThread;->mInstrumentation:Landroid/app/Instrumentation;
+Landroid/app/ActivityThread;->mNumVisibleActivities:I
+Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
+Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
+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/AlarmManager;->FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED:I
+Landroid/app/AlarmManager;->FLAG_IDLE_UNTIL:I
+Landroid/app/AlarmManager;->FLAG_STANDALONE:I
+Landroid/app/AlarmManager;->FLAG_WAKE_FROM_IDLE:I
+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;
+Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController;
+Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application;
+Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager;
+Landroid/app/Application;->attach(Landroid/content/Context;)V
+Landroid/app/ApplicationLoaders;->getDefault()Landroid/app/ApplicationLoaders;
+Landroid/app/ApplicationLoaders;->mLoaders:Landroid/util/ArrayMap;
+Landroid/app/Application;->mComponentCallbacks:Ljava/util/ArrayList;
+Landroid/app/Application;->mLoadedApk:Landroid/app/LoadedApk;
+Landroid/app/ApplicationPackageManager;->deletePackage(Ljava/lang/String;Landroid/content/pm/IPackageDeleteObserver;I)V
+Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;)I
+Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;I)I
+Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->noteOp(I)I
+Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I
+Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
+Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
+Landroid/app/AppOpsManager;->setMode(IILjava/lang/String;I)V
+Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
+Landroid/app/backup/BackupDataInput;-><init>(Ljava/io/FileDescriptor;)V
+Landroid/app/backup/BackupDataInputStream;->dataSize:I
+Landroid/app/backup/BackupDataInputStream;->key:Ljava/lang/String;
+Landroid/app/backup/BackupDataOutput;-><init>(Ljava/io/FileDescriptor;)V
+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;-><init>()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/BackupObserver;-><init>()V
+Landroid/app/backup/BackupTransport;-><init>()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/RestoreDescription;-><init>(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;-><init>(Ljava/lang/String;Ljava/lang/String;J)V
+Landroid/app/backup/SelectBackupTransportCallback;-><init>()V
+Landroid/app/ContextImpl;->createActivityContext(Landroid/app/ActivityThread;Landroid/app/LoadedApk;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;ILandroid/content/res/Configuration;)Landroid/app/ContextImpl;
+Landroid/app/ContextImpl;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
+Landroid/app/ContextImpl;->mPackageInfo:Landroid/app/LoadedApk;
+Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V
+Landroid/app/DatePickerDialog;->mDatePicker:Landroid/widget/DatePicker;
+Landroid/app/Dialog;->CANCEL:I
+Landroid/app/Dialog;->mCancelMessage:Landroid/os/Message;
+Landroid/app/Dialog;->mDismissMessage:Landroid/os/Message;
+Landroid/app/Dialog;->mListenersHandler:Landroid/os/Handler;
+Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity;
+Landroid/app/Dialog;->mShowMessage:Landroid/os/Message;
+Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
+Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
+Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
+Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
+Landroid/app/IActivityManager;->resumeAppSwitches()V
+Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
+Landroid/app/InstantAppResolverService;-><init>()V
+Landroid/app/InstantAppResolverService$InstantAppResolutionCallback;->onInstantAppResolveInfo(Ljava/util/List;)V
+Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler;
+Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
+Landroid/app/LoadedApk;->mApplication:Landroid/app/Application;
+Landroid/app/LoadedApk;->mReceivers:Landroid/util/ArrayMap;
+Landroid/app/LoadedApk;->mResDir:Ljava/lang/String;
+Landroid/app/LoadedApk;->mResources:Landroid/content/res/Resources;
+Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map;
+Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList;
+Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList;
+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/Notification;->setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V
+Landroid/app/PendingIntent;->getActivityAsUser(Landroid/content/Context;ILandroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/PendingIntent;
+Landroid/app/PendingIntent;->getIntent()Landroid/content/Intent;
+Landroid/app/PendingIntent;->isActivity()Z
+Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context;
+Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView;
+Landroid/app/Service;->setForeground(Z)V
+Landroid/app/StatusBarManager;->collapsePanels()V
+Landroid/app/StatusBarManager;->disable(I)V
+Landroid/app/StatusBarManager;->expandNotificationsPanel()V
+Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V
+Landroid/app/StatusBarManager;->expandSettingsPanel()V
+Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
+Landroid/app/usage/UsageStatsManager;->getAppStandbyBuckets()Ljava/util/Map;
+Landroid/app/usage/UsageStatsManager;->setAppStandbyBuckets(Ljava/util/Map;)V
+Landroid/app/usage/UsageStatsManager;->whitelistAppTemporarily(Ljava/lang/String;JLandroid/os/UserHandle;)V
+Landroid/app/WallpaperColors;->getColorHints()I
+Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap;
+Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap;
+Landroid/app/WallpaperManager;->getIWallpaperManager()Landroid/app/IWallpaperManager;
+Landroid/app/WallpaperManager;->openDefaultWallpaper(Landroid/content/Context;I)Ljava/io/InputStream;
+Landroid/app/WallpaperManager;->setBitmap(Landroid/graphics/Bitmap;Landroid/graphics/Rect;ZII)I
+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/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;->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$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
+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/ContentProviderOperation;->mSelection:Ljava/lang/String;
+Landroid/content/ContentProviderOperation;->mType:I
+Landroid/content/ContentResolver;->getContentService()Landroid/content/IContentService;
+Landroid/content/ContentResolver;->getSyncStatus(Landroid/accounts/Account;Ljava/lang/String;)Landroid/content/SyncStatusInfo;
+Landroid/content/ContentResolver;->mContext:Landroid/content/Context;
+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;->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;->mBase:Landroid/content/Context;
+Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal;
+Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver;
+Landroid/content/IContentService;->cancelSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/content/ComponentName;)V
+Landroid/content/IContentService;->getMasterSyncAutomatically()Z
+Landroid/content/IContentService;->setMasterSyncAutomatically(Z)V
+Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
+Landroid/content/IntentFilter;->setOrder(I)V
+Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
+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;->targetSandboxVersion:I
+Landroid/content/pm/InstantAppIntentFilter;-><init>(Ljava/lang/String;Ljava/util/List;)V
+Landroid/content/pm/InstantAppResolveInfo;->getPackageName()Ljava/lang/String;
+Landroid/content/pm/InstantAppResolveInfo;-><init>(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;-><init>(Ljava/lang/String;)V
+Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
+Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
+Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
+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;->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;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/PackageManager;->freeStorage(JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->freeStorage(Ljava/lang/String;JLandroid/content/IntentSender;)V
+Landroid/content/pm/PackageManager;->getApplicationInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
+Landroid/content/pm/PackageManager;->getPackageCandidateVolumes(Landroid/content/pm/ApplicationInfo;)Ljava/util/List;
+Landroid/content/pm/PackageManager;->getPackageInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageManager;->getPackageSizeInfo(Ljava/lang/String;Landroid/content/pm/IPackageStatsObserver;)V
+Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang/String;I)Landroid/content/res/Resources;
+Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)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;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
+Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
+Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/UserInfo;->id:I
+Landroid/content/pm/UserInfo;->isPrimary()Z
+Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I
+Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray;
+Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence;
+Landroid/content/res/AssetManager;->mObject:J
+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/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
+Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
+Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
+Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache;
+Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration;
+Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache;
+Landroid/content/res/ResourcesImpl;->mPreloading:Z
+Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray;
+Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray;
+Landroid/content/res/ResourcesImpl;->TRACE_FOR_MISS_PRELOAD:Z
+Landroid/content/res/ResourcesImpl;->TRACE_FOR_PRELOAD:Z
+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;->mTmpValue:Landroid/util/TypedValue;
+Landroid/content/res/Resources;->mTypedArrayPool:Landroid/util/Pools$SynchronizedPool;
+Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V
+Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z
+Landroid/content/res/TypedArray;->mAssets:Landroid/content/res/AssetManager;
+Landroid/content/res/TypedArray;->mData:[I
+Landroid/content/res/TypedArray;->mIndices:[I
+Landroid/content/res/TypedArray;->mLength:I
+Landroid/content/res/TypedArray;->mMetrics:Landroid/util/DisplayMetrics;
+Landroid/content/res/TypedArray;->mRecycled:Z
+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;->newParser()Landroid/content/res/XmlResourceParser;
+Landroid/content/res/XmlBlock$Parser;->mParseState:J
+Landroid/content/SyncStatusInfo;->lastSuccessTime:J
+Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
+Landroid/database/CursorWindow;->mWindowPtr:J
+Landroid/database/CursorWindow;->sCursorWindowSize:I
+Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray;
+Landroid/database/CursorWrapper;->mCursor:Landroid/database/Cursor;
+Landroid/graphics/Bitmap$Config;->nativeInt:I
+Landroid/graphics/Bitmap;->createAshmemBitmap()Landroid/graphics/Bitmap;
+Landroid/graphics/Bitmap;->createAshmemBitmap(Landroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
+Landroid/graphics/Bitmap;->getDefaultDensity()I
+Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I
+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;->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/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;->getStateSet(I)[I
+Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState;
+Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V
+Landroid/graphics/FontFamily;->abortCreation()V
+Landroid/graphics/FontFamily;->addFontFromAssetManager(Landroid/content/res/AssetManager;Ljava/lang/String;IZIII[Landroid/graphics/fonts/FontVariationAxis;)Z
+Landroid/graphics/FontFamily;->addFontFromBuffer(Ljava/nio/ByteBuffer;I[Landroid/graphics/fonts/FontVariationAxis;II)Z
+Landroid/graphics/FontFamily;->freeze()Z
+Landroid/graphics/FontFamily;-><init>()V
+Landroid/graphics/LinearGradient;->mColors:[I
+Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
+Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I
+Landroid/graphics/Typeface;->mStyle:I
+Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface;
+Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
+Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
+Landroid/hardware/Camera;->addCallbackBuffer([BI)V
+Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
+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/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
+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;-><init>()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;-><init>(II[B)V
+Landroid/hardware/location/GeofenceHardware;->addGeofence(IILandroid/hardware/location/GeofenceHardwareRequest;Landroid/hardware/location/GeofenceHardwareCallback;)Z
+Landroid/hardware/location/GeofenceHardwareCallback;-><init>()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;-><init>()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;-><init>(JIIJ)V
+Landroid/hardware/location/NanoApp;-><init>(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/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V
+Landroid/location/GeocoderParams;->getClientPackage()Ljava/lang/String;
+Landroid/location/GeocoderParams;->getLocale()Ljava/util/Locale;
+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/media/AudioAttributes$Builder;->setInternalCapturePreset(I)Landroid/media/AudioAttributes$Builder;
+Landroid/media/AudioFocusInfo;->getClientId()Ljava/lang/String;
+Landroid/media/AudioFocusInfo;->getClientUid()I
+Landroid/media/AudioFocusInfo;->getLossReceived()I
+Landroid/media/AudioManager;->abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;)I
+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/audiopolicy/AudioMix$Builder;->build()Landroid/media/audiopolicy/AudioMix;
+Landroid/media/audiopolicy/AudioMix$Builder;-><init>(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$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;-><init>()V
+Landroid/media/audiopolicy/AudioPolicy$AudioPolicyFocusListener;-><init>()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;-><init>(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/AudioSystem;->setDeviceConnectionState(IILjava/lang/String;Ljava/lang/String;)I
+Landroid/media/AudioTrack;->getLatency()I
+Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
+Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V
+Landroid/media/MediaFile;->getFileType(Ljava/lang/String;)Landroid/media/MediaFile$MediaFileType;
+Landroid/media/MediaFile;->getMimeTypeForFile(Ljava/lang/String;)Ljava/lang/String;
+Landroid/media/MediaFile;->isVideoFileType(I)Z
+Landroid/media/MediaFile$MediaFileType;->fileType:I
+Landroid/media/MediaFile;->sFileTypeMap:Ljava/util/HashMap;
+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
+Landroid/media/MediaPlayer;->newRequest()Landroid/os/Parcel;
+Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/util/List;)V
+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
+Landroid/media/MediaScanner;->mClient:Landroid/media/MediaScanner$MyMediaScannerClient;
+Landroid/media/MediaScanner;->scanSingleFile(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;
+Landroid/media/MiniThumbFile;->reset()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/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
+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;-><init>()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;
+Landroid/net/ConnectivityManager;->getLinkProperties(I)Landroid/net/LinkProperties;
+Landroid/net/ConnectivityManager;->getMobileDataEnabled()Z
+Landroid/net/ConnectivityManager;->getTetherableUsbRegexs()[Ljava/lang/String;
+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;-><init>()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/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/NetworkKey;-><init>(Landroid/net/WifiKey;)V
+Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
+Landroid/net/NetworkRecommendationProvider;-><init>(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;
+Landroid/net/NetworkStats;->metered:[I
+Landroid/net/NetworkStats;->operations:[J
+Landroid/net/NetworkStats;->roaming:[I
+Landroid/net/NetworkStats;->rxBytes:[J
+Landroid/net/NetworkStats;->rxPackets:[J
+Landroid/net/NetworkStats;->set:[I
+Landroid/net/NetworkStats;->size:I
+Landroid/net/NetworkStats;->tag:[I
+Landroid/net/NetworkStats;->txBytes:[J
+Landroid/net/NetworkStats;->txPackets:[J
+Landroid/net/NetworkStats;->uid:[I
+Landroid/net/RssiCurve;-><init>(II[BI)V
+Landroid/net/RssiCurve;-><init>(II[B)V
+Landroid/net/RssiCurve;->lookupScore(IZ)B
+Landroid/net/ScoredNetwork;-><init>(Landroid/net/NetworkKey;Landroid/net/RssiCurve;)V
+Landroid/net/ScoredNetwork;-><init>(Landroid/net/NetworkKey;Landroid/net/RssiCurve;ZLandroid/os/Bundle;)V
+Landroid/net/ScoredNetwork;-><init>(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;
+Landroid/net/SSLCertificateSocketFactory;->getHttpSocketFactory(ILandroid/net/SSLSessionCache;)Lorg/apache/http/conn/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;-><init>(ILandroid/net/SSLSessionCache;Z)V
+Landroid/net/SSLCertificateSocketFactory;->INSECURE_TRUST_MANAGER:[Ljavax/net/ssl/TrustManager;
+Landroid/net/SSLCertificateSocketFactory;->isSslCheckRelaxed()Z
+Landroid/net/SSLCertificateSocketFactory;->makeSocketFactory([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;)Ljavax/net/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;->mAlpnProtocols:[B
+Landroid/net/SSLCertificateSocketFactory;->mChannelIdPrivateKey:Ljava/security/PrivateKey;
+Landroid/net/SSLCertificateSocketFactory;->mHandshakeTimeoutMillis:I
+Landroid/net/SSLCertificateSocketFactory;->mInsecureFactory:Ljavax/net/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;->mKeyManagers:[Ljavax/net/ssl/KeyManager;
+Landroid/net/SSLCertificateSocketFactory;->mNpnProtocols:[B
+Landroid/net/SSLCertificateSocketFactory;->mSecureFactory:Ljavax/net/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;->mSecure:Z
+Landroid/net/SSLCertificateSocketFactory;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache;
+Landroid/net/SSLCertificateSocketFactory;->mTrustManagers:[Ljavax/net/ssl/TrustManager;
+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/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService;
+Landroid/net/TrafficStats;->setThreadStatsTagBackup()V
+Landroid/net/TrafficStats;->setThreadStatsTagRestore()V
+Landroid/net/TrafficStats;->setThreadStatsUid(I)V
+Landroid/net/WifiKey;-><init>(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;-><init>()V
+Landroid/net/wifi/RttManager;->startRanging([Landroid/net/wifi/RttManager$RttParams;Landroid/net/wifi/RttManager$RttListener;)V
+Landroid/net/wifi/WifiConfiguration;->apBand:I
+Landroid/net/wifi/WifiConfiguration;->apChannel:I
+Landroid/net/wifi/WifiConfiguration;->hasNoInternetAccess()Z
+Landroid/net/wifi/WifiConfiguration;->isEphemeral()Z
+Landroid/net/wifi/WifiConfiguration;->isNoInternetAccessExpected()Z
+Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
+Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
+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;-><init>()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/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;
+Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
+Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
+Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
+Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable;
+Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor;
+Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V
+Landroid/os/BatteryStats$HistoryItem;->states2:I
+Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I
+Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
+Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
+Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray;
+Landroid/os/BatteryStats$Uid;->getUid()I
+Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
+Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J
+Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J
+Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
+Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I
+Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
+Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
+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$MemoryInfo;->getOtherLabel(I)Ljava/lang/String;
+Landroid/os/Debug$MemoryInfo;->getOtherPrivateDirty(I)I
+Landroid/os/Debug$MemoryInfo;->getOtherPss(I)I
+Landroid/os/Debug$MemoryInfo;->getOtherSharedDirty(I)I
+Landroid/os/Debug$MemoryInfo;->NUM_DVK_STATS:I
+Landroid/os/Debug$MemoryInfo;->NUM_OTHER_STATS:I
+Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File;
+Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J
+Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z
+Landroid/os/FileUtils;->copyToFile(Ljava/io/InputStream;Ljava/io/File;)Z
+Landroid/os/FileUtils;->deleteOlderFiles(Ljava/io/File;IJ)Z
+Landroid/os/FileUtils;->readTextFile(Ljava/io/File;ILjava/lang/String;)Ljava/lang/String;
+Landroid/os/FileUtils;->setPermissions(Ljava/io/FileDescriptor;III)I
+Landroid/os/FileUtils;->setPermissions(Ljava/io/File;III)I
+Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I
+Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
+Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+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/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
+Landroid/os/IPowerManager;->userActivity(JII)V
+Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue;
+Landroid/os/MemoryFile;->getFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Message;->callback:Ljava/lang/Runnable;
+Landroid/os/Message;->flags:I
+Landroid/os/Message;->next:Landroid/os/Message;
+Landroid/os/MessageQueue;->mIdleHandlers:Ljava/util/ArrayList;
+Landroid/os/MessageQueue;->mMessages:Landroid/os/Message;
+Landroid/os/MessageQueue;->mQuitAllowed:Z
+Landroid/os/MessageQueue;->next()Landroid/os/Message;
+Landroid/os/Message;->target:Landroid/os/Handler;
+Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I
+Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I
+Landroid/os/PowerManager;->isLightDeviceIdleMode()Z
+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
+Landroid/os/PowerManager;->wakeUp(J)V
+Landroid/os/Process;->getParentPid(I)I
+Landroid/os/Process;->getPids(Ljava/lang/String;[I)[I
+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;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/ServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/storage/DiskInfo;->getDescription()Ljava/lang/String;
+Landroid/os/storage/StorageManager;->findVolumeByUuid(Ljava/lang/String;)Landroid/os/storage/VolumeInfo;
+Landroid/os/storage/StorageManager;->getBestVolumeDescription(Landroid/os/storage/VolumeInfo;)Ljava/lang/String;
+Landroid/os/storage/StorageManager;->getDisks()Ljava/util/List;
+Landroid/os/storage/StorageManager;->getStorageBytesUntilLow(Ljava/io/File;)J
+Landroid/os/storage/StorageManager;->getVolumeList(II)[Landroid/os/storage/StorageVolume;
+Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume;
+Landroid/os/storage/StorageManager;->getVolumePaths()[Ljava/lang/String;
+Landroid/os/storage/StorageManager;->getVolumes()Ljava/util/List;
+Landroid/os/storage/StorageManager;->getVolumeState(Ljava/lang/String;)Ljava/lang/String;
+Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File;
+Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String;
+Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getDisk()Landroid/os/storage/DiskInfo;
+Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
+Landroid/os/storage/VolumeInfo;->getState()I
+Landroid/os/storage/VolumeInfo;->getType()I
+Landroid/os/storage/VolumeInfo;->isPrimary()Z
+Landroid/os/storage/VolumeInfo;->isVisible()Z
+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;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
+Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
+Landroid/os/Trace;->isTagEnabled(J)Z
+Landroid/os/Trace;->setAppTracingAllowed(Z)V
+Landroid/os/Trace;->traceBegin(JLjava/lang/String;)V
+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;-><init>()V
+Landroid/os/UpdateEngineCallback;->onPayloadApplicationComplete(I)V
+Landroid/os/UpdateEngineCallback;->onStatusUpdate(IF)V
+Landroid/os/UpdateEngine;->cancel()V
+Landroid/os/UpdateEngine;-><init>()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;
+Landroid/os/UpdateLock;->release()V
+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;->getUserId(I)I
+Landroid/os/UserHandle;-><init>(I)V
+Landroid/os/UserHandle;->isOwner()Z
+Landroid/os/UserHandle;->myUserId()I
+Landroid/os/UserHandle;->of(I)Landroid/os/UserHandle;
+Landroid/os/UserHandle;->USER_OWNER:I
+Landroid/os/UserManager;->getBadgedLabelForUser(Ljava/lang/CharSequence;Landroid/os/UserHandle;)Ljava/lang/CharSequence;
+Landroid/os/UserManager;->get(Landroid/content/Context;)Landroid/os/UserManager;
+Landroid/os/UserManager;->getMaxSupportedUsers()I
+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/WorkSource;->add(ILjava/lang/String;)Z
+Landroid/os/WorkSource;->add(I)Z
+Landroid/os/WorkSource;->get(I)I
+Landroid/os/WorkSource;->getName(I)Ljava/lang/String;
+Landroid/os/WorkSource;->size()I
+Landroid/preference/DialogPreference;->mBuilder:Landroid/app/AlertDialog$Builder;
+Landroid/preference/DialogPreference;->mDialogIcon:Landroid/graphics/drawable/Drawable;
+Landroid/preference/DialogPreference;->mDialog:Landroid/app/Dialog;
+Landroid/preference/DialogPreference;->mDialogMessage:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mDialogTitle:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mNegativeButtonText:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mPositiveButtonText:Ljava/lang/CharSequence;
+Landroid/preference/DialogPreference;->mWhichButtonClicked:I
+Landroid/preference/ListPreference;->mClickedDialogEntryIndex:I
+Landroid/preference/PreferenceActivity;->mPreferenceManager:Landroid/preference/PreferenceManager;
+Landroid/preference/PreferenceActivity;->mPrefsContainer:Landroid/view/ViewGroup;
+Landroid/preference/PreferenceManager;->dispatchActivityDestroy()V
+Landroid/preference/PreferenceManager;->dispatchActivityResult(IILandroid/content/Intent;)V
+Landroid/preference/PreferenceManager;->dispatchActivityStop()V
+Landroid/preference/PreferenceManager;->getEditor()Landroid/content/SharedPreferences$Editor;
+Landroid/preference/PreferenceManager;->getPreferenceScreen()Landroid/preference/PreferenceScreen;
+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;->mActivityDestroyListeners:Ljava/util/List;
+Landroid/preference/PreferenceManager;->mOnPreferenceTreeClickListener:Landroid/preference/PreferenceManager$OnPreferenceTreeClickListener;
+Landroid/preference/PreferenceManager;->mSharedPreferences:Landroid/content/SharedPreferences;
+Landroid/preference/PreferenceManager;->registerOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V
+Landroid/preference/PreferenceManager;->registerOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V
+Landroid/preference/PreferenceManager;->setFragment(Landroid/preference/PreferenceFragment;)V
+Landroid/preference/PreferenceManager;->setPreferences(Landroid/preference/PreferenceScreen;)Z
+Landroid/preference/PreferenceManager;->shouldCommit()Z
+Landroid/preference/PreferenceManager;->unregisterOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V
+Landroid/preference/PreferenceManager;->unregisterOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V
+Landroid/preference/Preference;->onKey(Landroid/view/View;ILandroid/view/KeyEvent;)Z
+Landroid/preference/Preference;->performClick(Landroid/preference/PreferenceScreen;)V
+Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter;
+Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
+Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
+Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
+Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z
+Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V
+Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V
+Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V
+Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
+Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/provider/Settings$Global;->OTA_DISABLE_AUTOMATIC_UPDATE:Ljava/lang/String;
+Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:Ljava/lang/String;
+Landroid/provider/Settings$Secure;->PACKAGE_VERIFIER_USER_CONSENT:Ljava/lang/String;
+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;->putStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;I)Z
+Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Inbox;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Inbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Sent;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+Landroid/provider/Telephony$Sms$Sent;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri;
+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;->TextAppearance:[I
+Landroid/R$styleable;->TextAppearance_textColor:I
+Landroid/R$styleable;->TextAppearance_textSize:I
+Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnectFailed()V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnect(Ljava/lang/String;Landroid/media/session/MediaSession$Token;Landroid/os/Bundle;)V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildren(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;)V
+Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildrenWithOptions(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;Landroid/os/Bundle;)V
+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;->getMaximumDataBlockSize()J
+Landroid/service/persistentdata/PersistentDataBlockManager;->read()[B
+Landroid/service/persistentdata/PersistentDataBlockManager;->write([B)I
+Landroid/service/resolver/ResolverRankerService;-><init>()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;-><init>()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/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;-><init>()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;-><init>()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/telephony/PhoneStateListener;->mSubId:Ljava/lang/Integer;
+Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
+Landroid/telephony/SignalStrength;->getCdmaLevel()I
+Landroid/telephony/SignalStrength;->getLteDbm()I
+Landroid/telephony/SignalStrength;->getLteRsrp()I
+Landroid/telephony/SignalStrength;->getLteRssnr()I
+Landroid/telephony/SignalStrength;->getLteSignalStrength()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;->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;->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;->getDefault()Landroid/telephony/TelephonyManager;
+Landroid/telephony/TelephonyManager;->getITelephony()Lcom/android/internal/telephony/ITelephony;
+Landroid/telephony/TelephonyManager;->getLine1Number(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkClass(I)I
+Landroid/telephony/TelephonyManager;->getNetworkCountryIso(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkOperatorName(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getNetworkType(I)I
+Landroid/telephony/TelephonyManager;->getSimOperator(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSimOperatorName(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSimSerialNumber(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getSubscriberId(I)Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
+Landroid/telephony/TelephonyManager;->setDataEnabled(IZ)V
+Landroid/text/AndroidBidi;->bidi(I[C[B)I
+Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
+Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
+Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions;
+Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod;
+Landroid/text/SpannableStringBuilder;->mGapLength:I
+Landroid/text/SpannableStringBuilder;->mGapStart:I
+Landroid/text/SpannableStringBuilder;->mSpanCount:I
+Landroid/text/SpannableStringBuilder;->mSpanEnds:[I
+Landroid/text/SpannableStringBuilder;->mSpanFlags:[I
+Landroid/text/SpannableStringBuilder;->mSpans:[Ljava/lang/Object;
+Landroid/text/SpannableStringBuilder;->mSpanStarts:[I
+Landroid/text/SpannableStringInternal;->charAt(I)C
+Landroid/text/SpannableStringInternal;->checkRange(Ljava/lang/String;II)V
+Landroid/text/SpannableStringInternal;->COLUMNS:I
+Landroid/text/SpannableStringInternal;->copySpans(Landroid/text/SpannableStringInternal;II)V
+Landroid/text/SpannableStringInternal;->copySpans(Landroid/text/Spanned;II)V
+Landroid/text/SpannableStringInternal;->EMPTY:[Ljava/lang/Object;
+Landroid/text/SpannableStringInternal;->END:I
+Landroid/text/SpannableStringInternal;->FLAGS:I
+Landroid/text/SpannableStringInternal;->getChars(II[CI)V
+Landroid/text/SpannableStringInternal;->getSpanEnd(Ljava/lang/Object;)I
+Landroid/text/SpannableStringInternal;->getSpanFlags(Ljava/lang/Object;)I
+Landroid/text/SpannableStringInternal;->getSpans(IILjava/lang/Class;)[Ljava/lang/Object;
+Landroid/text/SpannableStringInternal;->getSpanStart(Ljava/lang/Object;)I
+Landroid/text/SpannableStringInternal;-><init>(Ljava/lang/CharSequence;II)V
+Landroid/text/SpannableStringInternal;->isIndexFollowsNextLine(I)Z
+Landroid/text/SpannableStringInternal;->isOutOfCopyRange(IIII)Z
+Landroid/text/SpannableStringInternal;->length()I
+Landroid/text/SpannableStringInternal;->mSpanCount:I
+Landroid/text/SpannableStringInternal;->mSpanData:[I
+Landroid/text/SpannableStringInternal;->mSpans:[Ljava/lang/Object;
+Landroid/text/SpannableStringInternal;->mText:Ljava/lang/String;
+Landroid/text/SpannableStringInternal;->nextSpanTransition(IILjava/lang/Class;)I
+Landroid/text/SpannableStringInternal;->region(II)Ljava/lang/String;
+Landroid/text/SpannableStringInternal;->removeSpan(Ljava/lang/Object;)V
+Landroid/text/SpannableStringInternal;->sendSpanAdded(Ljava/lang/Object;II)V
+Landroid/text/SpannableStringInternal;->sendSpanChanged(Ljava/lang/Object;IIII)V
+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/StaticLayout;->mColumns:I
+Landroid/text/StaticLayout;->mLineCount:I
+Landroid/text/StaticLayout;->mLines:[I
+Landroid/text/TextLine;->obtain()Landroid/text/TextLine;
+Landroid/text/TextLine;->sCached:[Landroid/text/TextLine;
+Landroid/text/TextPaint;->setUnderlineText(IF)V
+Landroid/util/Log;->wtf(ILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ZZ)I
+Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object;
+Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()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/ActionMode;->isUiFocusable()Z
+Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
+Landroid/view/Choreographer;->doFrame(JI)V
+Landroid/view/Choreographer;->getFrameTime()J
+Landroid/view/Choreographer;->mCallbackQueues:[Landroid/view/Choreographer$CallbackQueue;
+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/ContextThemeWrapper;->mResources:Landroid/content/res/Resources;
+Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/view/ContextThemeWrapper;->mThemeResource:I
+Landroid/view/InputDevice;->isExternal()Z
+Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
+Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
+Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
+Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
+Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
+Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V
+Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
+Landroid/view/IWindowManager;->getAnimationScale(I)F
+Landroid/view/IWindowManager;->hasNavigationBar()Z
+Landroid/view/IWindowManager;->setAnimationScale(IF)V
+Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
+Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+Landroid/view/LayoutInflater;->createViewFromTag(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;)Landroid/view/View;
+Landroid/view/LayoutInflater;->createViewFromTag(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;Z)Landroid/view/View;
+Landroid/view/LayoutInflater;->mConstructorArgs:[Ljava/lang/Object;
+Landroid/view/LayoutInflater;->mFactory2:Landroid/view/LayoutInflater$Factory2;
+Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory;
+Landroid/view/LayoutInflater;->mFactorySet:Z
+Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap;
+Landroid/view/MotionEvent;->HISTORY_CURRENT:I
+Landroid/view/MotionEvent;->mNativePtr:J
+Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F
+Landroid/view/MotionEvent;->scale(F)V
+Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
+Landroid/view/SurfaceView;->mCallbacks:Ljava/util/ArrayList;
+Landroid/view/SurfaceView;->mFormat:I
+Landroid/view/SurfaceView;->mRequestedFormat:I
+Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(IIILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(II)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
+Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextClassification;)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;->mLayer:Landroid/view/HardwareLayer;
+Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
+Landroid/view/TextureView;->mUpdateListener:Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;
+Landroid/view/TouchDelegate;->mDelegateTargeted:Z
+Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker;
+Landroid/view/View;->clearAccessibilityFocus()V
+Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z
+Landroid/view/View;->computeOpaqueFlags()V
+Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
+Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
+Landroid/view/ViewConfiguration;->sHasPermanentMenuKey:Z
+Landroid/view/View;->createSnapshot(Landroid/view/ViewDebug$CanvasProvider;Z)Landroid/graphics/Bitmap;
+Landroid/view/ViewDebug;->dispatchCommand(Landroid/view/View;Ljava/lang/String;Ljava/lang/String;Ljava/io/OutputStream;)V
+Landroid/view/ViewDebug;->dump(Landroid/view/View;ZZLjava/io/OutputStream;)V
+Landroid/view/View;->dispatchAttachedToWindow(Landroid/view/View$AttachInfo;I)V
+Landroid/view/View;->dispatchDetachedFromWindow()V
+Landroid/view/View;->fitsSystemWindows()Z
+Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
+Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
+Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I
+Landroid/view/ViewGroup;->FLAG_USE_CHILD_DRAWING_ORDER:I
+Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V
+Landroid/view/ViewGroup$MarginLayoutParams;->endMargin:I
+Landroid/view/ViewGroup$MarginLayoutParams;->startMargin:I
+Landroid/view/ViewGroup;->mChildren:[Landroid/view/View;
+Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
+Landroid/view/ViewGroup;->mGroupFlags:I
+Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
+Landroid/view/View;->isPaddingResolved()Z
+Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z
+Landroid/view/View;->isVisibleToUser()Z
+Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener;
+Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
+Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
+Landroid/view/View;->mAttachInfo:Landroid/view/View$AttachInfo;
+Landroid/view/View;->mBottom:I
+Landroid/view/View;->mContext:Landroid/content/Context;
+Landroid/view/View;->mDrawingCache:Landroid/graphics/Bitmap;
+Landroid/view/View;->mLeft:I
+Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo;
+Landroid/view/View;->mMinHeight:I
+Landroid/view/View;->mMinWidth:I
+Landroid/view/View;->mPaddingLeft:I
+Landroid/view/View;->mPaddingRight:I
+Landroid/view/View;->mParent:Landroid/view/ViewParent;
+Landroid/view/View;->mPrivateFlags3:I
+Landroid/view/View;->mRecreateDisplayList:Z
+Landroid/view/View;->mResources:Landroid/content/res/Resources;
+Landroid/view/View;->mRight:I
+Landroid/view/View;->mScrollCache:Landroid/view/View$ScrollabilityCache;
+Landroid/view/View;->mScrollX:I
+Landroid/view/View;->mScrollY:I
+Landroid/view/View;->mStartActivityRequestWho:Ljava/lang/String;
+Landroid/view/View;->mTop:I
+Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap;
+Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V
+Landroid/view/View;->recomputePadding()V
+Landroid/view/View;->requestAccessibilityFocus()Z
+Landroid/view/View;->resetPaddingToInitialValues()V
+Landroid/view/ViewRootImpl;->detachFunctor(J)V
+Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
+Landroid/view/ViewRootImpl;->mStopped:Z
+Landroid/view/View;->setAssistBlocked(Z)V
+Landroid/view/View;->setFrame(IIII)Z
+Landroid/view/View;->setIsRootNamespace(Z)V
+Landroid/view/View;->startActivityForResult(Landroid/content/Intent;I)V
+Landroid/view/View;->STATUS_BAR_DISABLE_BACK:I
+Landroid/view/View;->STATUS_BAR_DISABLE_EXPAND:I
+Landroid/view/View;->STATUS_BAR_DISABLE_HOME:I
+Landroid/view/View;->STATUS_BAR_DISABLE_RECENT:I
+Landroid/view/View;->toGlobalMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/view/View;->toLocalMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/view/ViewTreeObserver;->addOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->setTouchableInsets(I)V
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->TOUCHABLE_INSETS_REGION:I
+Landroid/view/ViewTreeObserver$InternalInsetsInfo;->touchableRegion:Landroid/graphics/Region;
+Landroid/view/ViewTreeObserver;->removeOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V
+Landroid/view/WindowManagerGlobal;->getInstance()Landroid/view/WindowManagerGlobal;
+Landroid/view/WindowManagerGlobal;->getRootView(Ljava/lang/String;)Landroid/view/View;
+Landroid/view/WindowManagerGlobal;->getViewRootNames()[Ljava/lang/String;
+Landroid/view/WindowManagerGlobal;->getWindowManagerService()Landroid/view/IWindowManager;
+Landroid/view/WindowManagerGlobal;->mLock:Ljava/lang/Object;
+Landroid/view/WindowManagerGlobal;->mViews:Ljava/util/ArrayList;
+Landroid/view/WindowManagerGlobal;->trimMemory(I)V
+Landroid/view/WindowManager$LayoutParams;->needsMenuKey:I
+Landroid/view/WindowManager$LayoutParams;->NEEDS_MENU_SET_TRUE: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/WebSettings;->setNavDump(Z)V
+Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V
+Landroid/webkit/WebView;->debugDump()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;->sPackageInfo:Landroid/content/pm/PackageInfo;
+Landroid/webkit/WebView;->getVisibleTitleHeight()I
+Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->isPaused()Z
+Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->notifyFindDialogDismissed()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/widget/AbsListView$FlingRunnable;->endFling()V
+Landroid/widget/AbsListView;->invokeOnItemScrollListener()V
+Landroid/widget/AbsListView;->isVerticalScrollBarHidden()Z
+Landroid/widget/AbsListView;->mAdapter:Landroid/widget/ListAdapter;
+Landroid/widget/AbsListView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
+Landroid/widget/AbsListView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
+Landroid/widget/AbsListView;->mFastScroll:Landroid/widget/FastScroller;
+Landroid/widget/AbsListView;->mFlingRunnable:Landroid/widget/AbsListView$FlingRunnable;
+Landroid/widget/AbsListView;->mIsChildViewEnabled:Z
+Landroid/widget/AbsListView;->mMaximumVelocity:I
+Landroid/widget/AbsListView;->mMotionPosition:I
+Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener;
+Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin;
+Landroid/widget/AbsListView;->mSelectionTopPadding:I
+Landroid/widget/AbsListView;->mSelectorPosition:I
+Landroid/widget/AbsListView;->mSelectorRect:Landroid/graphics/Rect;
+Landroid/widget/AbsListView;->mTouchMode:I
+Landroid/widget/AbsListView;->mTouchSlop:I
+Landroid/widget/AbsListView;->mVelocityTracker:Landroid/view/VelocityTracker;
+Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJFF)Z
+Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJ)Z
+Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V
+Landroid/widget/AbsListView;->trackMotionScroll(II)Z
+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/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V
+Landroid/widget/AdapterView;->mDataChanged:Z
+Landroid/widget/AdapterView;->setNextSelectedPositionInt(I)V
+Landroid/widget/AdapterView;->setSelectedPositionInt(I)V
+Landroid/widget/AutoCompleteTextView;->doAfterTextChanged()V
+Landroid/widget/AutoCompleteTextView;->doBeforeTextChanged()V
+Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V
+Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
+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;->mShowCursor:J
+Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/Gallery;->mDownTouchView:Landroid/view/View;
+Landroid/widget/GridView;->mColumnWidth:I
+Landroid/widget/GridView;->mHorizontalSpacing:I
+Landroid/widget/GridView;->mNumColumns:I
+Landroid/widget/GridView;->mRequestedNumColumns:I
+Landroid/widget/GridView;->mVerticalSpacing:I
+Landroid/widget/HorizontalScrollView;->mEdgeGlowLeft:Landroid/widget/EdgeEffect;
+Landroid/widget/HorizontalScrollView;->mEdgeGlowRight:Landroid/widget/EdgeEffect;
+Landroid/widget/HorizontalScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/ImageView;->mAdjustViewBounds:Z
+Landroid/widget/ImageView;->mAlpha:I
+Landroid/widget/ImageView;->mMaxHeight:I
+Landroid/widget/ImageView;->mMaxWidth:I
+Landroid/widget/LinearLayout;->mGravity:I
+Landroid/widget/LinearLayout;->mUseLargestChild:Z
+Landroid/widget/ListPopupWindow;->mPopup:Landroid/widget/PopupWindow;
+Landroid/widget/ListPopupWindow;->setForceIgnoreOutsideTouch(Z)V
+Landroid/widget/ListView;->mAreAllItemsSelectable:Z
+Landroid/widget/ListView;->setSelectionInt(I)V
+Landroid/widget/MediaController;->mAnchor:Landroid/view/View;
+Landroid/widget/MediaController;->mDecor:Landroid/view/View;
+Landroid/widget/MediaController;->mDecorLayoutParams:Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/MediaController;->mWindowManager:Landroid/view/WindowManager;
+Landroid/widget/NumberPicker;->mInputText:Landroid/widget/EditText;
+Landroid/widget/NumberPicker;->mSelectionDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/NumberPicker;->mSelectorWheelPaint:Landroid/graphics/Paint;
+Landroid/widget/PopupMenu;->mPopup:Lcom/android/internal/view/menu/MenuPopupHelper;
+Landroid/widget/PopupWindow;->computeAnimationResource()I
+Landroid/widget/PopupWindow;->createPopupLayoutParams(Landroid/os/IBinder;)Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/PopupWindow;->invokePopup(Landroid/view/WindowManager$LayoutParams;)V
+Landroid/widget/PopupWindow;->mAboveAnchor:Z
+Landroid/widget/PopupWindow;->mAnchor:Ljava/lang/ref/WeakReference;
+Landroid/widget/PopupWindow;->mAnimationStyle:I
+Landroid/widget/PopupWindow;->mBackgroundView:Landroid/view/View;
+Landroid/widget/PopupWindow;->mContentView:Landroid/view/View;
+Landroid/widget/PopupWindow;->mHeightMode:I
+Landroid/widget/PopupWindow;->mIsDropdown:Z
+Landroid/widget/PopupWindow;->mIsShowing:Z
+Landroid/widget/PopupWindow;->mLastHeight:I
+Landroid/widget/PopupWindow;->mLastWidth:I
+Landroid/widget/PopupWindow;->mOnScrollChangedListener:Landroid/view/ViewTreeObserver$OnScrollChangedListener;
+Landroid/widget/PopupWindow;->mWidthMode:I
+Landroid/widget/PopupWindow;->preparePopup(Landroid/view/WindowManager$LayoutParams;)V
+Landroid/widget/PopupWindow;->setLayoutInScreenEnabled(Z)V
+Landroid/widget/PopupWindow;->setLayoutInsetDecor(Z)V
+Landroid/widget/PopupWindow;->setTouchModal(Z)V
+Landroid/widget/ProgressBar;->mCurrentDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/ProgressBar;->mDuration:I
+Landroid/widget/ProgressBar;->mIndeterminate:Z
+Landroid/widget/ProgressBar;->mMaxHeight:I
+Landroid/widget/ProgressBar;->mMinHeight:I
+Landroid/widget/ProgressBar;->mOnlyIndeterminate:Z
+Landroid/widget/RelativeLayout$LayoutParams;->mBottom:I
+Landroid/widget/RelativeLayout$LayoutParams;->mLeft:I
+Landroid/widget/RelativeLayout$LayoutParams;->mRight:I
+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/RemoteViews;->mActions:Ljava/util/ArrayList;
+Landroid/widget/RemoteViews;->mApplication:Landroid/content/pm/ApplicationInfo;
+Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String;
+Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
+Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
+Landroid/widget/ScrollView;->mIsBeingDragged:Z
+Landroid/widget/ScrollView;->mOverflingDistance:I
+Landroid/widget/ScrollView;->mOverscrollDistance:I
+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;->onCloseClicked()V
+Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V
+Landroid/widget/SlidingDrawer;->mTopOffset:I
+Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
+Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
+Landroid/widget/TextView;->assumeLayout()V
+Landroid/widget/TextView;->createEditorIfNeeded()V
+Landroid/widget/TextView;->mCursorDrawableRes:I
+Landroid/widget/TextView;->mCurTextColor:I
+Landroid/widget/TextView;->mEditor:Landroid/widget/Editor;
+Landroid/widget/TextView;->mListeners:Ljava/util/ArrayList;
+Landroid/widget/TextView;->mMarquee:Landroid/widget/TextView$Marquee;
+Landroid/widget/TextView;->mMaximum:I
+Landroid/widget/TextView;->mMaxMode:I
+Landroid/widget/TextView;->mSingleLine:Z
+Landroid/widget/VideoView;->mCurrentBufferPercentage:I
+Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController;
+Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback;
+Landroid/widget/VideoView;->mUri:Landroid/net/Uri;
+Landroid/widget/VideoView;->mVideoHeight:I
+Landroid/widget/VideoView;->mVideoWidth:I
+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/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
+Lcom/android/internal/os/BatterySipper;-><init>(Lcom/android/internal/os/BatterySipper$DrainType;Landroid/os/BatteryStats$Uid;D)V
+Lcom/android/internal/os/BatterySipper;->mPackages:[Ljava/lang/String;
+Lcom/android/internal/os/BatterySipper;->packageWithHighestDrain:Ljava/lang/String;
+Lcom/android/internal/os/BatterySipper;->totalPowerMah:D
+Lcom/android/internal/os/BatterySipper;->uidObj:Landroid/os/BatteryStats$Uid;
+Lcom/android/internal/os/BatteryStatsHelper;->getMaxPower()D
+Lcom/android/internal/os/BatteryStatsHelper;->getStats()Landroid/os/BatteryStats;
+Lcom/android/internal/os/BatteryStatsHelper;->getTotalPower()D
+Lcom/android/internal/os/BatteryStatsHelper;-><init>(Landroid/content/Context;ZZ)V
+Lcom/android/internal/os/BatteryStatsHelper;->load()V
+Lcom/android/internal/os/BatteryStatsHelper;->mBatteryInfo:Lcom/android/internal/app/IBatteryStats;
+Lcom/android/internal/os/BatteryStatsHelper;->mPowerProfile:Lcom/android/internal/os/PowerProfile;
+Lcom/android/internal/os/BatteryStatsHelper;->mUsageList:Ljava/util/List;
+Lcom/android/internal/os/BatteryStatsHelper;->refreshStats(II)V
+Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/os/BatteryStatsImpl;->getDischargeAmount(I)I
+Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I
+Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getSensorStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getUid()I
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWakelockStats()Landroid/util/ArrayMap;
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiRunningTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiScanTime(JI)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getForegroundTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getStarts(I)I
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getSystemTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getUserTime(I)J
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getHandle()I
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getSensorTime()Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/BatteryStatsImpl$Uid$Wakelock;->getWakeTime(I)Lcom/android/internal/os/BatteryStatsImpl$Timer;
+Lcom/android/internal/os/FuseAppLoop;->onCommand(IJJJI[B)V
+Lcom/android/internal/os/FuseAppLoop;->onOpen(JJ)[B
+Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;)D
+Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;I)D
+Lcom/android/internal/os/PowerProfile;->getBatteryCapacity()D
+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$attr;->switchStyle:I
+Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I
+Lcom/android/internal/R$bool;->config_showNavigationBar:I
+Lcom/android/internal/R$dimen;->navigation_bar_height:I
+Lcom/android/internal/R$dimen;->navigation_bar_height_landscape:I
+Lcom/android/internal/R$dimen;->status_bar_height:I
+Lcom/android/internal/R$drawable;->ic_menu_close_clear_cancel:I
+Lcom/android/internal/R$id;->amPm:I
+Lcom/android/internal/R$id;->edittext_container:I
+Lcom/android/internal/R$id;->icon:I
+Lcom/android/internal/R$id;->message:I
+Lcom/android/internal/R$id;->minute:I
+Lcom/android/internal/R$id;->shortcut:I
+Lcom/android/internal/R$id;->text:I
+Lcom/android/internal/R$id;->time:I
+Lcom/android/internal/R$id;->timePicker:I
+Lcom/android/internal/R$id;->title_container:I
+Lcom/android/internal/R$id;->title:I
+Lcom/android/internal/R$integer;->config_screenBrightnessDim:I
+Lcom/android/internal/R$layout;->screen_title:I
+Lcom/android/internal/R$styleable;->CompoundButton_button:I
+Lcom/android/internal/R$styleable;->CompoundButton:[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;->ScrollView:[I
+Lcom/android/internal/R$styleable;->TabWidget:[I
+Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
+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;->View_background:I
+Lcom/android/internal/R$styleable;->View:[I
+Lcom/android/internal/R$styleable;->View_id:I
+Lcom/android/internal/R$styleable;->ViewStub:[I
+Lcom/android/internal/R$styleable;->ViewStub_inflatedId:I
+Lcom/android/internal/R$styleable;->ViewStub_layout:I
+Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$xml;->power_profile:I
+Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
+Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
+Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
+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/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
+Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context;
+Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+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/okhttp/ConnectionPool;->maxIdleConnections:I
+Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V
+Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
+Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
+Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
+Ldalvik/system/BlockGuard$Policy;->onNetwork()V
+Ldalvik/system/CloseGuard;->close()V
+Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
+Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
+Ldalvik/system/CloseGuard;->warnIfOpen()V
+Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
+Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
+Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
+Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList$Element;->dexFile: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;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
+Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
+Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
+Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
+Ldalvik/system/VMDebug;->dumpReferenceTables()V
+Ldalvik/system/VMRuntime;->clearGrowthLimit()V
+Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
+Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
+Ldalvik/system/VMRuntime;->is64Bit()Z
+Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
+Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
+Ldalvik/system/VMRuntime;->registerNativeFree(I)V
+Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
+Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
+Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
+Ldalvik/system/VMRuntime;->trackExternalFree(J)V
+Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
+Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
+Ljava/io/FileDescriptor;->descriptor:I
+Ljava/io/FileDescriptor;->getInt$()I
+Ljava/io/FileDescriptor;->setInt$(I)V
+Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
+Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object;
+Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
+Ljava/lang/Class;->dexCache:Ljava/lang/Object;
+Ljava/lang/Class;->dexClassDefIndex:I
+Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
+Ljava/lang/Daemons$Daemon;->stop()V
+Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
+Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
+Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
+Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
+Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
+Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup;
+Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup;
+Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
+Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
+Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
+Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
+Ljava/net/InetAddress;->clearDnsCache()V
+Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z
+Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
+Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
+Ljava/net/URI;->host:Ljava/lang/String;
+Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
+Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
+Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
+Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
+Ljava/util/ArrayList$SubList;->parentOffset:I
+Ljava/util/ArrayList$SubList;->size:I
+Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object;
+Ljava/util/Calendar;->zone:Ljava/util/TimeZone;
+Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
+Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
+Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
+Lorg/json/JSONArray;->values:Ljava/util/List;
+Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 06a9b06..0b10a35 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,7 @@
package android.accessibilityservice;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -43,6 +44,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -346,6 +349,19 @@
*/
public String[] packageNames;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FEEDBACK_" }, value = {
+ FEEDBACK_AUDIBLE,
+ FEEDBACK_GENERIC,
+ FEEDBACK_HAPTIC,
+ FEEDBACK_SPOKEN,
+ FEEDBACK_VISUAL,
+ FEEDBACK_BRAILLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeedbackType {}
+
/**
* The feedback type an {@link AccessibilityService} provides.
* <p>
@@ -358,6 +374,7 @@
* @see #FEEDBACK_VISUAL
* @see #FEEDBACK_BRAILLE
*/
+ @FeedbackType
public int feedbackType;
/**
@@ -818,7 +835,8 @@
return stringBuilder.toString();
}
- private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
+ private static void appendFeedbackTypes(StringBuilder stringBuilder,
+ @FeedbackType int feedbackTypes) {
stringBuilder.append("feedbackTypes:");
stringBuilder.append("[");
while (feedbackTypes != 0) {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 782733f..5176d71e 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -680,7 +680,7 @@
*/
@NonNull
public Account[] getAccountsByType(String type) {
- return getAccountsByTypeAsUser(type, Process.myUserHandle());
+ return getAccountsByTypeAsUser(type, mContext.getUser());
}
/** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
@@ -2006,7 +2006,7 @@
final AccountManagerCallback<Bundle> callback,
final Handler handler) {
return confirmCredentialsAsUser(account, options, activity, callback, handler,
- Process.myUserHandle());
+ mContext.getUser());
}
/**
@@ -3208,7 +3208,7 @@
return finishSessionAsUser(
sessionBundle,
activity,
- Process.myUserHandle(),
+ mContext.getUser(),
callback,
handler);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fdb0ac9..aca8d48 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -113,6 +113,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillPopupWindow;
@@ -125,7 +126,6 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import dalvik.system.VMRuntime;
@@ -425,6 +425,12 @@
* safely called after {@link #onPause()} and allows and application to safely
* wait until {@link #onStop()} to save persistent state.</p>
*
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
* <p>For those methods that are not marked as being killable, the activity's
* process will not be killed by the system starting from the time the method
* is called and continuing after it returns. Thus an activity is in the killable
@@ -1577,8 +1583,11 @@
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
- * <p>If called, this method will occur before {@link #onStop}. There are
- * no guarantees about whether it will occur before or after {@link #onPause}.
+ * <p>If called, this method will occur after {@link #onStop} for applications
+ * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+ * For applications targeting earlier platform versions this method will occur
+ * before {@link #onStop} and there are no guarantees about whether it will
+ * occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
@@ -5823,7 +5832,7 @@
ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
mParent == null ? mToken : mParent.mToken,
mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
- UserHandle.myUserId());
+ getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
// Empty
@@ -5952,12 +5961,16 @@
*
* @return Returns the complete component name for this activity
*/
- @Override
- public ComponentName getComponentName()
- {
+ public ComponentName getComponentName() {
return mComponent;
}
+ /** @hide */
+ @Override
+ public final ComponentName autofillClientGetComponentName() {
+ return getComponentName();
+ }
+
/**
* Retrieve a {@link SharedPreferences} object for accessing preferences
* that are private to this activity. This simply calls the underlying
@@ -6253,7 +6266,6 @@
*
* @param action the action to run on the UI thread
*/
- @Override
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
@@ -6262,6 +6274,12 @@
}
}
+ /** @hide */
+ @Override
+ public final void autofillClientRunOnUiThread(Runnable action) {
+ runOnUiThread(action);
+ }
+
/**
* Standard implementation of
* {@link android.view.LayoutInflater.Factory#onCreateView} used when
@@ -6344,6 +6362,8 @@
} else {
writer.print(prefix); writer.println("No AutofillManager");
}
+
+ ResourcesManager.getInstance().dump(prefix, writer);
}
/**
@@ -7067,6 +7087,18 @@
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
+
+ setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
+ enableAutofillCompatibilityIfNeeded();
+ }
+
+ private void enableAutofillCompatibilityIfNeeded() {
+ if (isAutofillCompatibilityEnabled()) {
+ final AutofillManager afm = getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.enableCompatibilityMode();
+ }
+ }
}
/** @hide */
@@ -7563,7 +7595,7 @@
/** @hide */
@Override
- final public void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
+ public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Intent fillInIntent) {
try {
startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
@@ -7575,13 +7607,13 @@
/** @hide */
@Override
- final public void autofillCallbackResetableStateAvailable() {
+ public final void autofillClientResetableStateAvailable() {
mAutoFillResetNeeded = true;
}
/** @hide */
@Override
- final public boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width,
+ public final boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width,
int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) {
final boolean wasShowing;
@@ -7598,7 +7630,7 @@
/** @hide */
@Override
- final public boolean autofillCallbackRequestHideFillUi() {
+ public final boolean autofillClientRequestHideFillUi() {
if (mAutofillPopupWindow == null) {
return false;
}
@@ -7609,8 +7641,16 @@
/** @hide */
@Override
- @NonNull public View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds) {
- final View[] views = new View[viewIds.length];
+ public final boolean autofillClientIsFillUiShowing() {
+ return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing();
+ }
+
+ /** @hide */
+ @Override
+ @NonNull
+ public final View[] autofillClientFindViewsByAutofillIdTraversal(
+ @NonNull AutofillId[] autofillId) {
+ final View[] views = new View[autofillId.length];
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
@@ -7618,10 +7658,11 @@
final View rootView = roots.get(rootNum).getView();
if (rootView != null) {
- for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
+ final int viewCount = autofillId.length;
+ for (int viewNum = 0; viewNum < viewCount; viewNum++) {
if (views[viewNum] == null) {
views[viewNum] = rootView.findViewByAutofillIdTraversal(
- viewIds[viewNum]);
+ autofillId[viewNum].getViewId());
}
}
}
@@ -7632,14 +7673,15 @@
/** @hide */
@Override
- @Nullable public View findViewByAutofillIdTraversal(int viewId) {
+ @Nullable
+ public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
final View rootView = roots.get(rootNum).getView();
if (rootView != null) {
- final View view = rootView.findViewByAutofillIdTraversal(viewId);
+ final View view = rootView.findViewByAutofillIdTraversal(autofillId.getViewId());
if (view != null) {
return view;
}
@@ -7651,50 +7693,62 @@
/** @hide */
@Override
- @NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
- final boolean[] isVisible = new boolean[viewIds.length];
- final View views[] = findViewsByAutofillIdTraversal(viewIds);
-
- for (int i = 0; i < viewIds.length; i++) {
- View view = views[i];
- if (view == null) {
- isVisible[i] = false;
- continue;
- }
-
- isVisible[i] = true;
-
- // Check if the view is visible by checking all parents
- while (true) {
- if (view instanceof DecorView && view.getViewRootImpl() == view.getParent()) {
- break;
- }
-
- if (view.getVisibility() != View.VISIBLE) {
- isVisible[i] = false;
- break;
- }
-
- if (view.getParent() instanceof View) {
- view = (View) view.getParent();
+ public final @NonNull boolean[] autofillClientGetViewVisibility(
+ @NonNull AutofillId[] autofillIds) {
+ final int autofillIdCount = autofillIds.length;
+ final boolean[] visible = new boolean[autofillIdCount];
+ for (int i = 0; i < autofillIdCount; i++) {
+ final AutofillId autofillId = autofillIds[i];
+ final View view = autofillClientFindViewByAutofillIdTraversal(autofillId);
+ if (view != null) {
+ if (!autofillId.isVirtual()) {
+ visible[i] = view.isVisibleToUser();
} else {
- break;
+ visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildId());
}
}
}
+ return visible;
+ }
- return isVisible;
+ /** @hide */
+ public final @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId,
+ int windowId) {
+ final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance()
+ .getRootViews(getActivityToken());
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+ if (rootView != null && rootView.getAccessibilityWindowId() == windowId) {
+ final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
+ if (view != null) {
+ return view;
+ }
+ }
+ }
+ return null;
}
/** @hide */
@Override
- public boolean isVisibleForAutofill() {
+ public final @Nullable IBinder autofillClientGetActivityToken() {
+ return getActivityToken();
+ }
+
+ /** @hide */
+ @Override
+ public final boolean autofillClientIsVisibleForAutofill() {
return !mStopped;
}
/** @hide */
@Override
- public boolean isDisablingEnterExitEventForAutofill() {
+ public final boolean autofillIsCompatibilityModeEnabled() {
+ return isAutofillCompatibilityEnabled();
+ }
+
+ /** @hide */
+ @Override
+ public final boolean isDisablingEnterExitEventForAutofill() {
return mAutoFillIgnoreFirstResumePause || !mResumed;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e2ce8b1..ae47a68 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1691,7 +1691,7 @@
if (maxNum < 0) {
throw new IllegalArgumentException("The requested number of tasks should be >= 0");
}
- return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList();
+ return getService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2685,7 +2685,7 @@
public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
try {
return getService().clearApplicationUserData(packageName, false,
- observer, UserHandle.myUserId());
+ observer, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2724,7 +2724,7 @@
try {
@SuppressWarnings("unchecked")
final ParceledListSlice<GrantedUriPermission> castedList = getService()
- .getGrantedUriPermissions(packageName, UserHandle.myUserId());
+ .getGrantedUriPermissions(packageName, mContext.getUserId());
return castedList;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2743,7 +2743,7 @@
public void clearGrantedUriPermissions(String packageName) {
try {
getService().clearGrantedUriPermissions(packageName,
- UserHandle.myUserId());
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3528,7 +3528,7 @@
public void killBackgroundProcesses(String packageName) {
try {
getService().killBackgroundProcesses(packageName,
- UserHandle.myUserId());
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3586,7 +3586,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.FORCE_STOP_PACKAGES)
public void forceStopPackage(String packageName) {
- forceStopPackageAsUser(packageName, UserHandle.myUserId());
+ forceStopPackageAsUser(packageName, mContext.getUserId());
}
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 29e091b..ff970bf 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,6 +113,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.LogPrinter;
+import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -204,7 +205,7 @@
private static final boolean DEBUG_SERVICE = false;
public static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
- private static final boolean DEBUG_ORDER = false;
+ public static final boolean DEBUG_ORDER = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
@@ -398,7 +399,6 @@
boolean startsNotResumed;
public final boolean isForward;
int pendingConfigChanges;
- boolean onlyLocalRequest;
Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
@@ -488,12 +488,14 @@
}
}
- public boolean isPreHoneycomb() {
- if (activity != null) {
- return activity.getApplicationInfo().targetSdkVersion
- < android.os.Build.VERSION_CODES.HONEYCOMB;
- }
- return false;
+ private boolean isPreHoneycomb() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ private boolean isPreP() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.P;
}
public boolean isPersistable() {
@@ -518,7 +520,6 @@
sb.append(", startsNotResumed=").append(startsNotResumed);
sb.append(", isForward=").append(isForward);
sb.append(", pendingConfigChanges=").append(pendingConfigChanges);
- sb.append(", onlyLocalRequest=").append(onlyLocalRequest);
sb.append(", preserveWindow=").append(mPreserveWindow);
if (activity != null) {
sb.append(", Activity{");
@@ -635,6 +636,8 @@
/** Initial values for {@link Profiler}. */
ProfilerInfo initProfilerInfo;
+ boolean autofillCompatibilityEnabled;
+
public String toString() {
return "AppBindData{appInfo=" + appInfo + "}";
}
@@ -761,15 +764,6 @@
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
- @Override
- public final void scheduleRelaunchActivity(IBinder token,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean preserveWindow) {
- requestRelaunchActivity(token, pendingResults, pendingNewIntents,
- configChanges, notResumed, config, overrideConfig, true, preserveWindow);
- }
-
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
@@ -861,7 +855,7 @@
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
- String buildSerial) {
+ String buildSerial, boolean autofillCompatibilityEnabled) {
if (services != null) {
// Setup the service cache in the ServiceManager
@@ -887,6 +881,7 @@
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
+ data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
sendMessage(H.BIND_APPLICATION, data);
}
@@ -1526,7 +1521,6 @@
public static final int UNBIND_SERVICE = 122;
public static final int DUMP_SERVICE = 123;
public static final int LOW_MEMORY = 124;
- public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
public static final int DESTROY_BACKUP_AGENT = 129;
@@ -1572,7 +1566,6 @@
case UNBIND_SERVICE: return "UNBIND_SERVICE";
case DUMP_SERVICE: return "DUMP_SERVICE";
case LOW_MEMORY: return "LOW_MEMORY";
- case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PROFILER_CONTROL: return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
@@ -1606,12 +1599,6 @@
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
- case RELAUNCH_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
- ActivityClientRecord r = (ActivityClientRecord)msg.obj;
- handleRelaunchActivity(r);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
@@ -3721,20 +3708,6 @@
}
r.activity.performResume(r.startsNotResumed);
- synchronized (mResourcesManager) {
- // If there is a pending local relaunch that was requested when the activity was
- // paused, it will put the activity into paused state when it finally happens.
- // Since the activity resumed before being relaunched, we don't want that to
- // happen, so we need to clear the request to relaunch paused.
- for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
- final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
- if (relaunching.token == r.token
- && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
- relaunching.startsNotResumed = false;
- }
- }
- }
-
EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
@@ -3883,14 +3856,12 @@
}
}
- if (!r.onlyLocalRequest) {
- r.nextIdle = mNewActivities;
- mNewActivities = r;
- if (localLOGV) Slog.v(
- TAG, "Scheduling idle handler for " + r);
- Looper.myQueue().addIdleHandler(new Idler());
+ r.nextIdle = mNewActivities;
+ mNewActivities = r;
+ if (localLOGV) {
+ Slog.v(TAG, "Scheduling idle handler for " + r);
}
- r.onlyLocalRequest = false;
+ Looper.myQueue().addIdleHandler(new Idler());
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
@@ -4165,9 +4136,12 @@
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+ // Before P onSaveInstanceState was called before onStop, starting with P it's
+ // called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
- if (shouldSaveState) {
+ final boolean isPreP = r.isPreP();
+ if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}
@@ -4186,6 +4160,10 @@
r.setState(ON_STOP);
EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
+
+ if (shouldSaveState && !isPreP) {
+ callActivityOnSaveInstanceState(r);
+ }
}
private void updateVisibility(ActivityClientRecord r, boolean show) {
@@ -4574,15 +4552,12 @@
mSomeActivitiesChanged = true;
}
- /**
- * @param preserveWindow Whether the activity should try to reuse the window it created,
- * including the decor view after the relaunch.
- */
- public final void requestRelaunchActivity(IBinder token,
+ @Override
+ public ActivityClientRecord prepareRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
+ int configChanges, MergedConfiguration config, boolean preserveWindow) {
ActivityClientRecord target = null;
+ boolean scheduleRelaunch = false;
synchronized (mResourcesManager) {
for (int i=0; i<mRelaunchingActivities.size(); i++) {
@@ -4604,57 +4579,31 @@
r.pendingIntents = pendingNewIntents;
}
}
-
- // For each relaunch request, activity manager expects an answer
- if (!r.onlyLocalRequest && fromServer) {
- try {
- ActivityManager.getService().activityRelaunched(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
break;
}
}
if (target == null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:"
- + fromServer);
+ if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null");
target = new ActivityClientRecord();
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
target.mPreserveWindow = preserveWindow;
- if (!fromServer) {
- final ActivityClientRecord existing = mActivities.get(token);
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing);
- if (existing != null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= "
- + existing.paused);;
- target.startsNotResumed = existing.paused;
- target.overrideConfig = existing.overrideConfig;
- }
- target.onlyLocalRequest = true;
- }
mRelaunchingActivities.add(target);
- sendMessage(H.RELAUNCH_ACTIVITY, target);
+ scheduleRelaunch = true;
}
-
- if (fromServer) {
- target.startsNotResumed = notResumed;
- target.onlyLocalRequest = false;
- }
- if (config != null) {
- target.createdConfig = config;
- }
- if (overrideConfig != null) {
- target.overrideConfig = overrideConfig;
- }
+ target.createdConfig = config.getGlobalConfiguration();
+ target.overrideConfig = config.getOverrideConfiguration();
target.pendingConfigChanges |= configChanges;
}
+
+ return scheduleRelaunch ? target : null;
}
- private void handleRelaunchActivity(ActivityClientRecord tmp) {
+ @Override
+ public void handleRelaunchActivity(ActivityClientRecord tmp,
+ PendingTransactionActions pendingActions) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
@@ -4723,18 +4672,10 @@
ActivityClientRecord r = mActivities.get(tmp.token);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
if (r == null) {
- if (!tmp.onlyLocalRequest) {
- try {
- ActivityManager.getService().activityRelaunched(tmp.token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
return;
}
r.activity.mConfigChangeFlags |= configChanges;
- r.onlyLocalRequest = tmp.onlyLocalRequest;
r.mPreserveWindow = tmp.mPreserveWindow;
r.activity.mChangingConfigurations = true;
@@ -4751,9 +4692,9 @@
// preserved by the server, so we want to notify it that we are preparing to replace
// everything
try {
- if (r.mPreserveWindow || r.onlyLocalRequest) {
+ if (r.mPreserveWindow) {
WindowManagerGlobal.getWindowSession().prepareToReplaceWindows(
- r.token, !r.onlyLocalRequest);
+ r.token, true /* childrenOnly */);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -4792,24 +4733,22 @@
r.startsNotResumed = tmp.startsNotResumed;
r.overrideConfig = tmp.overrideConfig;
- // TODO(lifecycler): Move relaunch to lifecycler.
- PendingTransactionActions pendingActions = new PendingTransactionActions();
handleLaunchActivity(r, pendingActions);
- handleStartActivity(r, pendingActions);
- handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
- if (r.startsNotResumed) {
- performPauseActivity(r, false /* finished */, "relaunch", pendingActions);
- }
+ // Only report a successful relaunch to WindowManager.
+ pendingActions.setReportRelaunchToWindowManager(true);
+ }
- if (!tmp.onlyLocalRequest) {
- try {
- ActivityManager.getService().activityRelaunched(r.token);
- if (r.window != null) {
- r.window.reportActivityRelaunched();
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ @Override
+ public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
+ try {
+ ActivityManager.getService().activityRelaunched(token);
+ final ActivityClientRecord r = mActivities.get(token);
+ if (pendingActions.shouldReportRelaunchToWindowManager() && r != null
+ && r.window != null) {
+ r.window.reportActivityRelaunched();
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -5831,6 +5770,10 @@
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
app = data.info.makeApplication(data.restrictedBackupMode, null);
+
+ // Propagate autofill compat state
+ app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
+
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
@@ -5874,21 +5817,23 @@
// Preload fonts resources
FontsContract.setApplicationContextForResources(appContext);
- try {
- final ApplicationInfo info =
- getPackageManager().getApplicationInfo(
- data.appInfo.packageName,
- PackageManager.GET_META_DATA /*flags*/,
- UserHandle.myUserId());
- if (info.metaData != null) {
- final int preloadedFontsResource = info.metaData.getInt(
- ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
- if (preloadedFontsResource != 0) {
- data.info.getResources().preloadFonts(preloadedFontsResource);
+ if (!Process.isIsolated()) {
+ try {
+ final ApplicationInfo info =
+ getPackageManager().getApplicationInfo(
+ data.appInfo.packageName,
+ PackageManager.GET_META_DATA /*flags*/,
+ UserHandle.myUserId());
+ if (info.metaData != null) {
+ final int preloadedFontsResource = info.metaData.getInt(
+ ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
+ if (preloadedFontsResource != 0) {
+ data.info.getResources().preloadFonts(preloadedFontsResource);
+ }
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 382719b..124f933 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -178,6 +178,7 @@
public static final int FLAG_IDLE_UNTIL = 1<<4;
private final IAlarmManager mService;
+ private final Context mContext;
private final String mPackageName;
private final boolean mAlwaysExact;
private final int mTargetSdkVersion;
@@ -265,6 +266,7 @@
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
+ mContext = ctx;
mPackageName = ctx.getPackageName();
mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT);
@@ -1028,7 +1030,7 @@
* @see #ACTION_NEXT_ALARM_CLOCK_CHANGED
*/
public AlarmClockInfo getNextAlarmClock() {
- return getNextAlarmClock(UserHandle.myUserId());
+ return getNextAlarmClock(mContext.getUserId());
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index e923fb2..4c9fb74 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1670,7 +1670,7 @@
/** @hide */
public void resetAllModes() {
try {
- mService.resetAllModes(UserHandle.myUserId(), null);
+ mService.resetAllModes(mContext.getUserId(), null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index a13ac49..81cbbca 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.view.autofill.AutofillManager;
/**
* Base class for maintaining global application state. You can provide your own
@@ -299,4 +300,37 @@
}
}
}
+
+ /** @hide */
+ @Override
+ public AutofillManager.AutofillClient getAutofillClient() {
+ final AutofillManager.AutofillClient client = super.getAutofillClient();
+ if (client != null) {
+ return client;
+ }
+ // Okay, ppl use the application context when they should not. This breaks
+ // autofill among other things. We pick the focused activity since autofill
+ // interacts only with the currently focused activity and we need the fill
+ // client only if a call comes from the focused activity. Sigh...
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ return null;
+ }
+ final int activityCount = activityThread.mActivities.size();
+ for (int i = 0; i < activityCount; i++) {
+ final ActivityThread.ActivityClientRecord record =
+ activityThread.mActivities.valueAt(i);
+ if (record == null) {
+ continue;
+ }
+ final Activity activity = record.activity;
+ if (activity == null) {
+ continue;
+ }
+ if (activity.getWindow().getDecorView().hasFocus()) {
+ return record.activity;
+ }
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6b0a2f9..13117bc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -140,6 +140,11 @@
}
@Override
+ public int getUserId() {
+ return mContext.getUserId();
+ }
+
+ @Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 5b61fdf..114d18b 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -21,6 +21,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.util.MergedConfiguration;
import com.android.internal.content.ReferrerIntent;
@@ -124,6 +125,39 @@
public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
/**
+ * Prepare activity relaunch to update internal bookkeeping. This is used to track multiple
+ * relaunch and config update requests.
+ * @param token Activity token.
+ * @param pendingResults Activity results to be delivered.
+ * @param pendingNewIntents New intent messages to be delivered.
+ * @param configChanges Mask of configuration changes that have occurred.
+ * @param config New configuration applied to the activity.
+ * @param preserveWindow Whether the activity should try to reuse the window it created,
+ * including the decor view after the relaunch.
+ * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
+ * relaunch, or {@code null} if relaunch cancelled.
+ */
+ public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ int configChanges, MergedConfiguration config, boolean preserveWindow);
+
+ /**
+ * Perform activity relaunch.
+ * @param r Activity client record prepared for relaunch.
+ * @param pendingActions Pending actions to be used on later stages of activity transaction.
+ * */
+ public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r,
+ PendingTransactionActions pendingActions);
+
+ /**
+ * Report that relaunch request was handled.
+ * @param token Target activity token.
+ * @param pendingActions Pending actions initialized on earlier stages of activity transaction.
+ * Used to check if we should report relaunch to WM.
+ * */
+ public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions);
+
+ /**
* Debugging output.
* @param pw {@link PrintWriter} to write logs to.
* @param prefix Prefix to prepend to output.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6496110..a1ba13d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -164,7 +164,7 @@
private final @Nullable IBinder mActivityToken;
- private final @Nullable UserHandle mUser;
+ private final @NonNull UserHandle mUser;
private final ApplicationContentResolver mContentResolver;
@@ -187,6 +187,7 @@
private @Nullable String mSplitName = null;
private AutofillClient mAutofillClient = null;
+ private boolean mIsAutofillCompatEnabled;
private final Object mSync = new Object();
@@ -408,6 +409,7 @@
return sp;
}
+ @GuardedBy("ContextImpl.class")
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
@@ -1565,8 +1567,7 @@
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
- return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
- Process.myUserHandle());
+ return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
}
/** @hide */
@@ -2038,8 +2039,7 @@
@Override
public Context createPackageContext(String packageName, int flags)
throws NameNotFoundException {
- return createPackageContextAsUser(packageName, flags,
- mUser != null ? mUser : Process.myUserHandle());
+ return createPackageContextAsUser(packageName, flags, mUser);
}
@Override
@@ -2235,6 +2235,12 @@
/** {@hide} */
@Override
+ public UserHandle getUser() {
+ return mUser;
+ }
+
+ /** {@hide} */
+ @Override
public int getUserId() {
return mUser.getIdentifier();
}
@@ -2251,6 +2257,18 @@
mAutofillClient = client;
}
+ /** @hide */
+ @Override
+ public boolean isAutofillCompatibilityEnabled() {
+ return mIsAutofillCompatEnabled;
+ }
+
+ /** @hide */
+ @Override
+ public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+ mIsAutofillCompatEnabled = autofillCompatEnabled;
+ }
+
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
@@ -2382,7 +2400,7 @@
}
}
- mContentResolver = new ApplicationContentResolver(this, mainThread, user);
+ mContentResolver = new ApplicationContentResolver(this, mainThread);
}
void setResources(Resources r) {
@@ -2498,13 +2516,10 @@
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
- private final UserHandle mUser;
- public ApplicationContentResolver(
- Context context, ActivityThread mainThread, UserHandle user) {
+ public ApplicationContentResolver(Context context, ActivityThread mainThread) {
super(context);
mMainThread = Preconditions.checkNotNull(mainThread);
- mUser = Preconditions.checkNotNull(user);
}
@Override
@@ -2550,7 +2565,7 @@
/** @hide */
protected int resolveUserIdFromAuthority(String auth) {
- return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
+ return ContentProvider.getUserIdFromAuthority(auth, getUserId());
}
}
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2b648ea..eb26026 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -609,18 +609,19 @@
/**
* A key was pressed down.
+ * <p>
+ * If the focused view didn't want this event, this method is called.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
*
- * <p>If the focused view didn't want this event, this method is called.
- *
- * <p>The default implementation consumed the KEYCODE_BACK to later
- * handle it in {@link #onKeyUp}.
- *
* @see #onKeyUp
* @see android.view.KeyEvent
*/
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
event.startTracking();
return true;
}
@@ -640,16 +641,18 @@
/**
* A key was released.
- *
- * <p>The default implementation handles KEYCODE_BACK to close the
- * dialog.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to close the dialog.
*
* @see #onKeyDown
- * @see KeyEvent
+ * @see android.view.KeyEvent
*/
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+ && event.isTracking()
&& !event.isCanceled()) {
onBackPressed();
return true;
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 893a41c..ae9b83e 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -67,7 +67,7 @@
int debugMode, boolean enableBinderTracking, boolean trackAllocation,
boolean restrictedBackupMode, boolean persistent, in Configuration config,
in CompatibilityInfo compatInfo, in Map services,
- in Bundle coreSettings, in String buildSerial);
+ in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled);
void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
void scheduleExit();
void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
@@ -83,9 +83,6 @@
int resultCode, in String data, in Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState);
void scheduleLowMemory();
- void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
- in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
- in Configuration config, in Configuration overrideConfig, boolean preserveWindow);
void scheduleSleeping(IBinder token, boolean sleeping);
void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
void setSchedulingGroup(int group);
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 76a3682..2ba4c00 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -88,7 +88,7 @@
public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix,
String token, InstantAppResolutionCallback callback) {
// if not overridden, forward to old methods and filter out non-web intents
- if (sanitizedIntent.isBrowsableWebIntent()) {
+ if (sanitizedIntent.isWebIntent()) {
onGetInstantAppResolveInfo(hostDigestPrefix, token, callback);
} else {
callback.onInstantAppResolveInfo(Collections.emptyList());
@@ -107,7 +107,7 @@
String token, InstantAppResolutionCallback callback) {
Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden");
// if not overridden, forward to old methods and filter out non-web intents
- if (sanitizedIntent.isBrowsableWebIntent()) {
+ if (sanitizedIntent.isWebIntent()) {
onGetInstantAppIntentFilter(hostDigestPrefix, token, callback);
} else {
callback.onInstantAppResolveInfo(Collections.emptyList());
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 67f5a2e..7f87814 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1679,7 +1679,7 @@
public void execStartActivities(Context who, IBinder contextThread,
IBinder token, Activity target, Intent[] intents, Bundle options) {
execStartActivitiesAsUser(who, contextThread, token, target, intents, options,
- UserHandle.myUserId());
+ who.getUserId());
}
/**
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 553099f..4a3fcaf 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -407,7 +407,7 @@
* password.
*/
public boolean isDeviceLocked() {
- return isDeviceLocked(UserHandle.myUserId());
+ return isDeviceLocked(mContext.getUserId());
}
/**
@@ -432,7 +432,7 @@
* @return true if a PIN, pattern or password was set.
*/
public boolean isDeviceSecure() {
- return isDeviceSecure(UserHandle.myUserId());
+ return isDeviceSecure(mContext.getUserId());
}
/**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6e40986..c805658 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1055,11 +1055,10 @@
/**
* {@link #extras} key: A
* {@link android.content.ContentUris content URI} pointing to an image that can be displayed
- * in the background when the notification is selected. The URI must point to an image stream
- * suitable for passing into
+ * in the background when the notification is selected. Used on television platforms.
+ * The URI must point to an image stream suitable for passing into
* {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
- * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
- * URI used for this purpose must require no permissions to read the image data.
+ * BitmapFactory.decodeStream}; all other content types will be ignored.
*/
public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
@@ -4617,10 +4616,15 @@
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
Action action = mActions.get(i);
- validRemoteInput |= hasValidRemoteInput(action);
+ boolean actionHasValidInput = hasValidRemoteInput(action);
+ validRemoteInput |= actionHasValidInput;
final RemoteViews button = generateActionButton(action, emphazisedMode,
i % 2 != 0, p.ambient);
+ if (actionHasValidInput) {
+ // Clear the drawable
+ button.setInt(R.id.action0, "setBackgroundResource", 0);
+ }
big.addView(R.id.actions, button);
}
} else {
@@ -6435,7 +6439,7 @@
public RemoteViews makeContentView(boolean increasedHeight) {
mBuilder.mOriginalActions = mBuilder.mActions;
mBuilder.mActions = new ArrayList<>();
- RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
+ RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
mBuilder.mActions = mBuilder.mOriginalActions;
mBuilder.mOriginalActions = null;
return remoteViews;
@@ -6470,11 +6474,11 @@
*/
@Override
public RemoteViews makeBigContentView() {
- return makeBigContentView(false /* showRightIcon */);
+ return makeMessagingView(false /* isCollapsed */);
}
@NonNull
- private RemoteViews makeBigContentView(boolean showRightIcon) {
+ private RemoteViews makeMessagingView(boolean isCollapsed) {
CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
? super.mBigContentTitle
: mConversationTitle;
@@ -6485,21 +6489,24 @@
nameReplacement = conversationTitle;
conversationTitle = null;
}
+ boolean hideLargeIcon = !isCollapsed || isOneToOne;
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getMessagingLayoutResource(),
mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
- .hideLargeIcon(!showRightIcon || isOneToOne)
+ .hideLargeIcon(hideLargeIcon)
.headerTextSecondary(conversationTitle)
- .alwaysShowReply(showRightIcon));
+ .alwaysShowReply(isCollapsed));
addExtras(mBuilder.mN.extras);
// also update the end margin if there is an image
int endMargin = R.dimen.notification_content_margin_end;
- if (mBuilder.mN.hasLargeIcon() && showRightIcon) {
+ if (isCollapsed) {
endMargin = R.dimen.notification_content_plus_picture_margin_end;
}
contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
mBuilder.resolveContrastColor());
+ contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
+ isCollapsed);
contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
mBuilder.mN.mLargeIcon);
contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
@@ -6566,7 +6573,7 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
+ RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
return remoteViews;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 49c03ab..b10e608 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -24,6 +24,7 @@
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -98,7 +99,7 @@
* This broadcast is only sent to the app whose block state has changed.
*
* Input: nothing
- * Output: nothing
+ * Output: {@link #EXTRA_BLOCKED_STATE}
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_APP_BLOCK_STATE_CHANGED =
@@ -113,24 +114,31 @@
* This broadcast is only sent to the app that owns the channel that has changed.
*
* Input: nothing
- * Output: {@link #EXTRA_BLOCK_STATE_CHANGED_ID}
+ * Output: {@link #EXTRA_NOTIFICATION_CHANNEL_ID}
+ * Output: {@link #EXTRA_BLOCKED_STATE}
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED =
"android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
/**
- * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} or
- * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED} containing the id of the
- * object which has a new blocked state.
+ * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} containing the id of the
+ * {@link NotificationChannel} which has a new blocked state.
*
- * The value will be the {@link NotificationChannel#getId()} of the channel for
- * {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} and
- * the {@link NotificationChannelGroup#getId()} of the group for
- * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED}.
+ * The value will be the {@link NotificationChannel#getId()} of the channel.
*/
- public static final String EXTRA_BLOCK_STATE_CHANGED_ID =
- "android.app.extra.BLOCK_STATE_CHANGED_ID";
+ public static final String EXTRA_NOTIFICATION_CHANNEL_ID =
+ "android.app.extra.NOTIFICATION_CHANNEL_ID";
+
+ /**
+ * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED} containing the id
+ * of the {@link NotificationChannelGroup} which has a new blocked state.
+ *
+ * The value will be the {@link NotificationChannelGroup#getId()} of the group.
+ */
+ public static final String EXTRA_NOTIFICATION_CHANNEL_GROUP_ID =
+ "android.app.extra.NOTIFICATION_CHANNEL_GROUP_ID";
+
/**
* Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} or
@@ -142,7 +150,6 @@
*/
public static final String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
-
/**
* Intent that is broadcast when a {@link NotificationChannelGroup} is
* {@link NotificationChannelGroup#isBlocked() blocked} or unblocked.
@@ -150,7 +157,8 @@
* This broadcast is only sent to the app that owns the channel group that has changed.
*
* Input: nothing
- * Output: {@link #EXTRA_BLOCK_STATE_CHANGED_ID}
+ * Output: {@link #EXTRA_NOTIFICATION_CHANNEL_GROUP_ID}
+ * Output: {@link #EXTRA_BLOCKED_STATE}
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED =
@@ -343,6 +351,14 @@
* the same tag and id has already been posted by your application and has not yet been
* canceled, it will be replaced by the updated information.
*
+ * All {@link android.service.notification.NotificationListenerService listener services} will
+ * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris}
+ * provided on this notification or the
+ * {@link NotificationChannel} this notification is posted to using
+ * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the
+ * notification is canceled, or you can revoke permissions with
+ * {@link Context#revokeUriPermission(Uri, int)}.
+ *
* @param tag A string identifier for this notification. May be {@code null}.
* @param id An identifier for this notification. The pair (tag, id) must be unique
* within your application.
@@ -351,7 +367,7 @@
*/
public void notify(String tag, int id, Notification notification)
{
- notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
+ notifyAsUser(tag, id, notification, mContext.getUser());
}
/**
@@ -363,11 +379,13 @@
String pkg = mContext.getPackageName();
// Fix the notification as best we can.
Notification.addFieldsFromContext(mContext, notification);
+
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
notification.sound.checkFileUriExposed("Notification.sound");
}
+
}
fixLegacySmallIcon(notification, pkg);
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
@@ -378,6 +396,7 @@
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
notification.reduceImageSizes(mContext);
+
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowRam = am.isLowRamDevice();
final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
@@ -412,7 +431,7 @@
*/
public void cancel(String tag, int id)
{
- cancelAsUser(tag, id, new UserHandle(UserHandle.myUserId()));
+ cancelAsUser(tag, id, mContext.getUser());
}
/**
@@ -440,7 +459,7 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
- service.cancelAllNotifications(pkg, UserHandle.myUserId());
+ service.cancelAllNotifications(pkg, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1264,7 +1283,7 @@
final String pkg = mContext.getPackageName();
try {
final ParceledListSlice<StatusBarNotification> parceledList
- = service.getAppActiveNotifications(pkg, UserHandle.myUserId());
+ = service.getAppActiveNotifications(pkg, mContext.getUserId());
final List<StatusBarNotification> list = parceledList.getList();
return list.toArray(new StatusBarNotification[list.size()]);
} catch (RemoteException e) {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index d6429ae..315259b 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -345,7 +345,7 @@
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, options, UserHandle.myUserId());
+ flags, options, context.getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -486,7 +486,7 @@
ActivityManager.getService().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
null, null, requestCode, intents, resolvedTypes, flags, options,
- UserHandle.myUserId());
+ context.getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -544,8 +544,7 @@
*/
public static PendingIntent getBroadcast(Context context, int requestCode,
Intent intent, @Flags int flags) {
- return getBroadcastAsUser(context, requestCode, intent, flags,
- new UserHandle(UserHandle.myUserId()));
+ return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser());
}
/**
@@ -644,7 +643,7 @@
serviceKind, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, null, UserHandle.myUserId());
+ flags, null, context.getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fb11272..b96e028 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -21,6 +21,7 @@
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.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 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 @@
* Predicate that returns true if a WeakReference is gc'ed.
*/
private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate =
- new Predicate<WeakReference<Resources>>() {
- @Override
- public boolean test(WeakReference<Resources> weakRef) {
- return weakRef == null || weakRef.get() == null;
- }
- };
+ weakRef -> weakRef == null || weakRef.get() == null;
/**
* The global compatibility settings.
@@ -89,6 +90,48 @@
*/
private final ArrayList<WeakReference<Resources>> 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<ApkKey, ApkAssets> 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<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
+
/**
* Resources and base configuration override associated with an Activity.
*/
@@ -260,6 +303,41 @@
}
}
+ 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<ApkAssets> 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) {
+ final String idmapPath = "/data/resource-cache/"
+ + path.substring(1).replace('/', '@')
+ + "@idmap";
+ apkAssets = ApkAssets.loadOverlayFromPath(idmapPath, 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 +348,15 @@
*/
@VisibleForTesting
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
- AssetManager assets = new AssetManager();
+ final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
// 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 {
+ apkAssets.add(loadApkAssets(key.mResDir, false /*sharedLib*/, false /*overlay*/));
+ } catch (IOException e) {
Log.e(TAG, "failed to add asset path " + key.mResDir);
return null;
}
@@ -284,7 +364,10 @@
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
- if (assets.addAssetPath(splitResDir) == 0) {
+ try {
+ apkAssets.add(loadApkAssets(splitResDir, false /*sharedLib*/,
+ false /*overlay*/));
+ } catch (IOException e) {
Log.e(TAG, "failed to add split asset path " + splitResDir);
return null;
}
@@ -293,7 +376,13 @@
if (key.mOverlayDirs != null) {
for (final String idmapPath : key.mOverlayDirs) {
- assets.addOverlayPath(idmapPath);
+ try {
+ apkAssets.add(loadApkAssets(idmapPath, false /*sharedLib*/, true /*overlay*/));
+ } catch (IOException e) {
+ Log.w(TAG, "failed to add overlay path " + idmapPath);
+
+ // continue.
+ }
}
}
@@ -302,16 +391,77 @@
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
- if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
+ try {
+ apkAssets.add(loadApkAssets(libDir, true /*sharedLib*/, false /*overlay*/));
+ } catch (IOException e) {
Log.w(TAG, "Asset path '" + libDir +
"' does not exist or contains no resources.");
+
+ // continue.
}
}
}
}
+
+ AssetManager assets = new AssetManager();
+ assets.setApkAssets(apkAssets.toArray(new ApkAssets[apkAssets.size()]),
+ false /*invalidateCaches*/);
return assets;
}
+ private static <T> int countLiveReferences(Collection<WeakReference<T>> collection) {
+ int count = 0;
+ for (WeakReference<T> 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) {
Configuration config;
final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
@@ -630,28 +780,16 @@
// 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/StatsManager.java b/core/java/android/app/StatsManager.java
index c525c89..c2c91c2 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -30,20 +30,27 @@
* @hide
*/
@SystemApi
-public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends.
+public final class StatsManager {
IStatsManager mService;
private static final String TAG = "StatsManager";
+ private static final boolean DEBUG = false;
- /** Long extra of uid that added the relevant stats config. */
- public static final String EXTRA_STATS_CONFIG_UID =
- "android.app.extra.STATS_CONFIG_UID";
- /** Long extra of the relevant stats config's configKey. */
- public static final String EXTRA_STATS_CONFIG_KEY =
- "android.app.extra.STATS_CONFIG_KEY";
- /** Long extra of the relevant statsd_config.proto's Subscription.id. */
+ /**
+ * Long extra of uid that added the relevant stats config.
+ */
+ public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
+ /**
+ * Long extra of the relevant stats config's configKey.
+ */
+ public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
+ /**
+ * Long extra of the relevant statsd_config.proto's Subscription.id.
+ */
public static final String EXTRA_STATS_SUBSCRIPTION_ID =
"android.app.extra.STATS_SUBSCRIPTION_ID";
- /** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */
+ /**
+ * Long extra of the relevant statsd_config.proto's Subscription.rule_id.
+ */
public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID =
"android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
/**
@@ -68,28 +75,34 @@
}
/**
+ * Temporary. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
+ return addConfiguration(configKey, config);
+ }
+
+ /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
* @param configKey An arbitrary integer that allows clients to track the configuration.
* @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
* dependencies eg, conditions and matchers).
- * @param pkg The package name to receive the broadcast.
- * @param cls The name of the class that receives the broadcast.
* @return true if successful
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
+ public boolean addConfiguration(long configKey, byte[] config) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when adding configuration");
return false;
}
- return service.addConfiguration(configKey, config, pkg, cls);
+ return service.addConfiguration(configKey, config);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when adding configuration");
return false;
}
}
@@ -107,12 +120,12 @@
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when removing configuration");
return false;
}
return service.removeConfiguration(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when removing configuration");
return false;
}
}
@@ -121,38 +134,34 @@
/**
* Set the PendingIntent to be used when broadcasting subscriber information to the given
* subscriberId within the given config.
- *
* <p>
* Suppose that the calling uid has added a config with key configKey, and that in this config
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
* a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
* that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
* when the anomaly is detected.
- *
* <p>
* When statsd sends the broadcast, the PendingIntent will used to send an intent with
* information of
- * {@link #EXTRA_STATS_CONFIG_UID},
- * {@link #EXTRA_STATS_CONFIG_KEY},
- * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
- * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
- * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
- *
+ * {@link #EXTRA_STATS_CONFIG_UID},
+ * {@link #EXTRA_STATS_CONFIG_KEY},
+ * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
+ * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
+ * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
* <p>
* This function can only be called by the owner (uid) of the config. It must be called each
* time statsd starts. The config must have been added first (via addConfiguration()).
*
- * @param configKey The integer naming the config to which this subscriber is attached.
- * @param subscriberId ID of the subscriber, as used in the config.
+ * @param configKey The integer naming the config to which this subscriber is attached.
+ * @param subscriberId ID of the subscriber, as used in the config.
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
* associated with the given subscriberId. May be null, in which case
* it undoes any previous setting of this subscriberId.
* @return true if successful
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean setBroadcastSubscriber(long configKey,
- long subscriberId,
- PendingIntent pendingIntent) {
+ public boolean setBroadcastSubscriber(
+ long configKey, long subscriberId, PendingIntent pendingIntent) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
@@ -175,6 +184,44 @@
}
/**
+ * Registers the operation that is called to retrieve the metrics data. This must be called
+ * each time statsd starts. The config must have been added first (via addConfiguration(),
+ * although addConfiguration could have been called on a previous boot). This operation allows
+ * statsd to send metrics data whenever statsd determines that the metrics in memory are
+ * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
+ * data, which also deletes the retrieved metrics from statsd's memory.
+ *
+ * @param configKey The integer naming the config to which this operation is attached.
+ * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
+ * associated with the given subscriberId. May be null, in which case
+ * it removes any associated pending intent with this configKey.
+ * @return true if successful
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when registering data listener.");
+ return false;
+ }
+ if (pendingIntent == null) {
+ return service.removeDataFetchOperation(configKey);
+ } else {
+ // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+ IBinder intentSender = pendingIntent.getTarget().asBinder();
+ return service.setDataFetchOperation(configKey, intentSender);
+ }
+
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when registering data listener.");
+ return false;
+ }
+ }
+ }
+
+ /**
* Clients can request data with a binder call. This getter is destructive and also clears
* the retrieved metrics from statsd memory.
*
@@ -187,12 +234,12 @@
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting data");
return null;
}
return service.getData(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data");
return null;
}
}
@@ -211,12 +258,12 @@
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting metadata");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting metadata");
return null;
}
return service.getMetadata();
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting metadata");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting metadata");
return null;
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4310434..aa52cde 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -89,7 +89,6 @@
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
-import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
import android.net.wifi.RttManager;
@@ -116,7 +115,6 @@
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RecoverySystem;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -638,13 +636,13 @@
registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
new CachedServiceFetcher<RttManager>() {
- @Override
- public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE);
- IRttManager service = IRttManager.Stub.asInterface(b);
- return new RttManager(ctx.getOuterContext(), service,
- ConnectivityThread.getInstanceLooper());
- }});
+ @Override
+ public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+ return new RttManager(ctx.getOuterContext(),
+ new WifiRttManager(ctx.getOuterContext(), service));
+ }});
registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
new CachedServiceFetcher<WifiRttManager>() {
@@ -724,8 +722,9 @@
service = IPrintManager.Stub.asInterface(ServiceManager
.getServiceOrThrow(Context.PRINT_SERVICE));
}
- return new PrintManager(ctx.getOuterContext(), service, UserHandle.myUserId(),
- UserHandle.getAppId(Process.myUid()));
+ final int userId = ctx.getUserId();
+ final int appId = UserHandle.getAppId(ctx.getApplicationInfo().uid);
+ return new PrintManager(ctx.getOuterContext(), service, userId, appId);
}});
registerService(Context.COMPANION_DEVICE_SERVICE, CompanionDeviceManager.class,
@@ -780,12 +779,12 @@
}});
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
- new StaticServiceFetcher<TvInputManager>() {
+ new CachedServiceFetcher<TvInputManager>() {
@Override
- public TvInputManager createService() throws ServiceNotFoundException {
+ public TvInputManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TV_INPUT_SERVICE);
ITvInputManager service = ITvInputManager.Stub.asInterface(iBinder);
- return new TvInputManager(service, UserHandle.myUserId());
+ return new TvInputManager(service, ctx.getUserId());
}});
registerService(Context.NETWORK_SCORE_SERVICE, NetworkScoreManager.class,
diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java
index ab59747..b99b327 100644
--- a/core/java/android/app/TaskStackBuilder.java
+++ b/core/java/android/app/TaskStackBuilder.java
@@ -230,7 +230,7 @@
* Context.startActivity(Intent, Bundle)} for more details.
*/
public void startActivities(Bundle options) {
- startActivities(options, new UserHandle(UserHandle.myUserId()));
+ startActivities(options, mSourceContext.getUser());
}
/**
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index f21746c..19240e2 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -51,6 +51,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.DeadSystemException;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -1002,7 +1003,7 @@
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- return sGlobals.mService.getWallpaperInfo(UserHandle.myUserId());
+ return sGlobals.mService.getWallpaperInfo(mContext.getUserId());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1150,7 +1151,7 @@
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
"res:" + resources.getResourceName(resid),
mContext.getOpPackageName(), null, false, result, which, completion,
- UserHandle.myUserId());
+ mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
boolean ok = false;
@@ -1255,7 +1256,7 @@
boolean allowBackup, @SetWallpaperFlags int which)
throws IOException {
return setBitmap(fullImage, visibleCropHint, allowBackup, which,
- UserHandle.myUserId());
+ mContext.getUserId());
}
/**
@@ -1329,11 +1330,7 @@
private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)
throws IOException {
- byte[] buffer = new byte[32768];
- int amt;
- while ((amt=data.read(buffer)) > 0) {
- fos.write(buffer, 0, amt);
- }
+ FileUtils.copy(data, fos);
}
/**
@@ -1406,7 +1403,7 @@
try {
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
mContext.getOpPackageName(), visibleCropHint, allowBackup,
- result, which, completion, UserHandle.myUserId());
+ result, which, completion, mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
try {
@@ -1643,7 +1640,7 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
public boolean setWallpaperComponent(ComponentName name) {
- return setWallpaperComponent(name, UserHandle.myUserId());
+ return setWallpaperComponent(name, mContext.getUserId());
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e190fd4..77e118c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -140,7 +140,7 @@
/** @hide test will override it. */
@VisibleForTesting
protected int myUserId() {
- return UserHandle.myUserId();
+ return mContext.getUserId();
}
/**
@@ -5511,7 +5511,7 @@
@SystemApi
public @Nullable ComponentName getProfileOwner() throws IllegalArgumentException {
throwIfParentInstance("getProfileOwner");
- return getProfileOwnerAsUser(Process.myUserHandle().getIdentifier());
+ return getProfileOwnerAsUser(mContext.getUserId());
}
/**
@@ -5539,7 +5539,7 @@
public @Nullable String getProfileOwnerName() throws IllegalArgumentException {
if (mService != null) {
try {
- return mService.getProfileOwnerName(Process.myUserHandle().getIdentifier());
+ return mService.getProfileOwnerName(mContext.getUserId());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -7682,6 +7682,7 @@
throw re.rethrowFromSystemServer();
}
}
+
/**
* Called by a device owner or profile owner of secondary users that is affiliated with the
* device to disable the keyguard altogether.
@@ -8320,6 +8321,22 @@
}
/**
+ * Forces a batch of security logs to be fetched from logd and makes it available for DPC.
+ * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+ * @hide
+ */
+ public long forceSecurityLogs() {
+ if (mService == null) {
+ return 0;
+ }
+ try {
+ return mService.forceSecurityLogs();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called by the system to obtain a {@link DevicePolicyManager} whose calls act on the parent
* profile.
*
@@ -9387,41 +9404,6 @@
}
/**
- * Allows/disallows printing.
- *
- * Called by a device owner or a profile owner.
- * Device owner changes policy for all users. Profile owner can override it if present.
- * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored.
- *
- * @param admin which {@link DeviceAdminReceiver} this request is associated with.
- * @param enabled whether printing should be allowed or not.
- * @throws SecurityException if {@code admin} is neither device, nor profile owner.
- */
- public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) {
- try {
- mService.setPrintingEnabled(admin, enabled);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns whether printing is enabled for this user.
- *
- * Always {@code false} if {@code FEATURE_PRINTING} is absent.
- * Otherwise, {@code true} by default.
- *
- * @return {@code true} iff printing is enabled.
- */
- public boolean isPrintingEnabled() {
- try {
- return mService.isPrintingEnabled();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
* Called by device owner to add an override APN.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5197de4..5218a73 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -349,6 +349,7 @@
boolean isSecurityLoggingEnabled(in ComponentName admin);
ParceledListSlice retrieveSecurityLogs(in ComponentName admin);
ParceledListSlice retrievePreRebootSecurityLogs(in ComponentName admin);
+ long forceSecurityLogs();
boolean isUninstallInQueue(String packageName);
void uninstallPackageWithActiveAdmins(String packageName);
@@ -401,9 +402,6 @@
CharSequence getStartUserSessionMessage(in ComponentName admin);
CharSequence getEndUserSessionMessage(in ComponentName admin);
- void setPrintingEnabled(in ComponentName admin, boolean enabled);
- boolean isPrintingEnabled();
-
List<String> setMeteredDataDisabled(in ComponentName admin, in List<String> packageNames);
List<String> getMeteredDataDisabled(in ComponentName admin);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 08effd9..202b894 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -77,6 +77,7 @@
TAG_KEY_DESTRUCTION,
TAG_CERT_AUTHORITY_INSTALLED,
TAG_CERT_AUTHORITY_REMOVED,
+ TAG_CRYPTO_SELF_TEST_COMPLETED,
})
public @interface SecurityLogTag {}
@@ -400,6 +401,14 @@
SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED;
/**
+ * Indicates that cryptographic functionality self test has completed. The log entry contains an
+ * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if
+ * succeeded) and accessible via {@link SecurityEvent#getData()}.
+ */
+ public static final int TAG_CRYPTO_SELF_TEST_COMPLETED =
+ SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED;
+
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
@@ -529,6 +538,7 @@
case TAG_USER_RESTRICTION_REMOVED:
return LEVEL_INFO;
case TAG_CERT_AUTHORITY_REMOVED:
+ case TAG_CRYPTO_SELF_TEST_COMPLETED:
return getSuccess() ? LEVEL_INFO : LEVEL_ERROR;
case TAG_CERT_AUTHORITY_INSTALLED:
case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index be62678..b64b7e3 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -34,4 +34,5 @@
210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3)
210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3)
210029 security_cert_authority_installed (success|1),(subject|3)
-210030 security_cert_authority_removed (success|1),(subject|3)
\ No newline at end of file
+210030 security_cert_authority_removed (success|1),(subject|3)
+210031 security_crypto_self_test_completed (success|1)
\ No newline at end of file
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 87f2271..8f49bc1 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -4,6 +4,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.content.ComponentName;
+import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.net.Uri;
@@ -500,9 +501,8 @@
ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
if (forAutoFill) {
- final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
- ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
- view.onProvideAutofillStructure(builder, autofillFlags);
+ final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
+ view.onProvideAutofillStructure(builder, viewFlags);
} else {
// This is a secure window, so it doesn't want a screenshot, and that
// means we should also not copy out its view hierarchy for Assist
@@ -512,9 +512,8 @@
}
}
if (forAutoFill) {
- final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
- ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
- view.dispatchProvideAutofillStructure(builder, autofillFlags);
+ final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
+ view.dispatchProvideAutofillStructure(builder, viewFlags);
} else {
view.dispatchProvideStructure(builder);
}
@@ -532,6 +531,12 @@
mRoot = new ViewNode(reader, 0);
}
+ int resolveViewAutofillFlags(Context context, int fillRequestFlags) {
+ return (fillRequestFlags & FillRequest.FLAG_MANUAL_REQUEST) != 0
+ || context.isAutofillCompatibilityEnabled()
+ ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+ }
+
void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
out.writeInt(mX);
out.writeInt(mY);
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
new file mode 100644
index 0000000..d8a7463
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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.app.servertransaction;
+
+import static android.app.ActivityThread.DEBUG_ORDER;
+
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.app.ResultInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.MergedConfiguration;
+import android.util.Slog;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Activity relaunch callback.
+ * @hide
+ */
+public class ActivityRelaunchItem extends ClientTransactionItem {
+
+ private static final String TAG = "ActivityRelaunchItem";
+
+ private List<ResultInfo> mPendingResults;
+ private List<ReferrerIntent> mPendingNewIntents;
+ private int mConfigChanges;
+ private MergedConfiguration mConfig;
+ private boolean mPreserveWindow;
+
+ /**
+ * A record that was properly configured for relaunch. Execution will be cancelled if not
+ * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
+ */
+ private ActivityThread.ActivityClientRecord mActivityClientRecord;
+
+ @Override
+ public void preExecute(ClientTransactionHandler client, IBinder token) {
+ mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults,
+ mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ if (mActivityClientRecord == null) {
+ if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
+ return;
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
+ client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public void postExecute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ client.reportRelaunch(token, pendingActions);
+ }
+
+ // ObjectPoolItem implementation
+
+ private ActivityRelaunchItem() {}
+
+ /** Obtain an instance initialized with provided params. */
+ public static ActivityRelaunchItem obtain(List<ResultInfo> pendingResults,
+ List<ReferrerIntent> pendingNewIntents, int configChanges, MergedConfiguration config,
+ boolean preserveWindow) {
+ ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class);
+ if (instance == null) {
+ instance = new ActivityRelaunchItem();
+ }
+ instance.mPendingResults = pendingResults;
+ instance.mPendingNewIntents = pendingNewIntents;
+ instance.mConfigChanges = configChanges;
+ instance.mConfig = config;
+ instance.mPreserveWindow = preserveWindow;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mPendingResults = null;
+ mPendingNewIntents = null;
+ mConfigChanges = 0;
+ mConfig = null;
+ mPreserveWindow = false;
+ mActivityClientRecord = null;
+ ObjectPool.recycle(this);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(mPendingResults, flags);
+ dest.writeTypedList(mPendingNewIntents, flags);
+ dest.writeInt(mConfigChanges);
+ dest.writeTypedObject(mConfig, flags);
+ dest.writeBoolean(mPreserveWindow);
+ }
+
+ /** Read from Parcel. */
+ private ActivityRelaunchItem(Parcel in) {
+ mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
+ mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+ mConfigChanges = in.readInt();
+ mConfig = in.readTypedObject(MergedConfiguration.CREATOR);
+ mPreserveWindow = in.readBoolean();
+ }
+
+ public static final Creator<ActivityRelaunchItem> CREATOR =
+ new Creator<ActivityRelaunchItem>() {
+ public ActivityRelaunchItem createFromParcel(Parcel in) {
+ return new ActivityRelaunchItem(in);
+ }
+
+ public ActivityRelaunchItem[] newArray(int size) {
+ return new ActivityRelaunchItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ActivityRelaunchItem other = (ActivityRelaunchItem) o;
+ return Objects.equals(mPendingResults, other.mPendingResults)
+ && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
+ && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig)
+ && mPreserveWindow == other.mPreserveWindow;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mPendingResults);
+ result = 31 * result + Objects.hashCode(mPendingNewIntents);
+ result = 31 * result + mConfigChanges;
+ result = 31 * result + Objects.hashCode(mConfig);
+ result = 31 * result + (mPreserveWindow ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityRelaunchItem{pendingResults=" + mPendingResults
+ + ",pendingNewIntents=" + mPendingNewIntents + ",configChanges=" + mConfigChanges
+ + ",config=" + mConfig + ",preserveWindow" + mPreserveWindow + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 073d28c..af7b7a2 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -44,6 +44,7 @@
private boolean mCallOnPostCreate;
private Bundle mOldState;
private StopInfo mStopInfo;
+ private boolean mReportRelaunchToWM;
public PendingTransactionActions() {
clear();
@@ -91,6 +92,24 @@
mStopInfo = stopInfo;
}
+ /**
+ * Check if we should report an activity relaunch to WindowManager. We report back for every
+ * relaunch request to ActivityManager, but only for those that were actually finished to we
+ * report to WindowManager.
+ */
+ public boolean shouldReportRelaunchToWindowManager() {
+ return mReportRelaunchToWM;
+ }
+
+ /**
+ * Set if we should report an activity relaunch to WindowManager. We report back for every
+ * relaunch request to ActivityManager, but only for those that were actually finished we report
+ * to WindowManager.
+ */
+ public void setReportRelaunchToWindowManager(boolean reportToWm) {
+ mReportRelaunchToWM = reportToWm;
+ }
+
/** Reports to server about activity stop. */
public static class StopInfo implements Runnable {
private static final String TAG = "ActivityStopInfo";
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 4461b16..38d9025 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -31,4 +31,7 @@
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
+
+ byte[] getBackupPayload(int user);
+ void applyRestore(in byte[] payload, int user);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e5f3eae..3f13fff 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -20,9 +20,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -193,10 +193,15 @@
* <p>
* Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
* they still care about after a reboot.
+ * <p>
+ * This may only be called by apps that are the default launcher for the device
+ * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
*
* @param uri The uri of the slice being pinned.
* @param specs The list of supported {@link SliceSpec}s of the callback.
* @see SliceProvider#onSlicePinned(Uri)
+ * @see Intent#ACTION_ASSIST
+ * @see Intent#CATEGORY_HOME
*/
public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
try {
@@ -211,10 +216,15 @@
* Remove a pin for a slice.
* <p>
* If the slice has no other pins/callbacks then the slice will be unpinned.
+ * <p>
+ * This may only be called by apps that are the default launcher for the device
+ * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
*
* @param uri The uri of the slice being unpinned.
* @see #pinSlice
* @see SliceProvider#onSliceUnpinned(Uri)
+ * @see Intent#ACTION_ASSIST
+ * @see Intent#CATEGORY_HOME
*/
public void unpinSlice(@NonNull Uri uri) {
try {
@@ -262,17 +272,13 @@
*/
public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
- IContentProvider provider = resolver.acquireProvider(uri);
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
} catch (RemoteException e) {
Log.e(TAG, "Unable to get slice descendants", e);
- } finally {
- resolver.releaseProvider(provider);
}
return Collections.emptyList();
}
@@ -288,17 +294,15 @@
public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
ContentResolver resolver = mContext.getContentResolver();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
- null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
Bundle.setDefusable(res, true);
if (res == null) {
return null;
@@ -308,8 +312,6 @@
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
@@ -344,15 +346,13 @@
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- final Bundle res = provider.call(mContext.getPackageName(),
- SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
if (res == null) {
return null;
}
@@ -361,8 +361,6 @@
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
@@ -399,17 +397,15 @@
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(mContext.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
+ final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
}
@@ -418,8 +414,6 @@
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
- } finally {
- resolver.releaseProvider(provider);
}
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index edb992b..6b573e9 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
package android.app.usage;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -104,12 +105,14 @@
* An event type denoting that a notification was viewed by the user.
* @hide
*/
+ @SystemApi
public static final int NOTIFICATION_SEEN = 10;
/**
* An event type denoting a change in App Standby Bucket.
* @hide
*/
+ @SystemApi
public static final int STANDBY_BUCKET_CHANGED = 11;
/** @hide */
@@ -257,6 +260,17 @@
return mShortcutId;
}
+ /**
+ * Returns the standby bucket of the app, if the event is of type
+ * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+ * @return the standby bucket associated with the event.
+ * @hide
+ */
+ @SystemApi
+ public int getStandbyBucket() {
+ return mBucket;
+ }
+
/** @hide */
public Event getObfuscatedIfInstantApp() {
if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index edb6a74..cf35902 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -301,7 +301,7 @@
*/
public boolean isAppInactive(String packageName) {
try {
- return mService.isAppInactive(packageName, UserHandle.myUserId());
+ return mService.isAppInactive(packageName, mContext.getUserId());
} catch (RemoteException e) {
// fall through and return default
}
@@ -313,7 +313,7 @@
*/
public void setAppInactive(String packageName, boolean inactive) {
try {
- mService.setAppInactive(packageName, inactive, UserHandle.myUserId());
+ mService.setAppInactive(packageName, inactive, mContext.getUserId());
} catch (RemoteException e) {
// fall through
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index a55bbda..a2c75a6 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -459,10 +459,9 @@
*/
public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
+ private final Context mContext;
private final String mPackageName;
-
private final IAppWidgetService mService;
-
private final DisplayMetrics mDisplayMetrics;
/**
@@ -481,6 +480,7 @@
* @hide
*/
public AppWidgetManager(Context context, IAppWidgetService service) {
+ mContext = context;
mPackageName = context.getOpPackageName();
mService = service;
mDisplayMetrics = context.getResources().getDisplayMetrics();
@@ -845,7 +845,7 @@
}
if (profile == null) {
- profile = Process.myUserHandle();
+ profile = mContext.getUser();
}
try {
@@ -924,7 +924,7 @@
if (mService == null) {
return;
}
- bindAppWidgetIdIfAllowed(appWidgetId, Process.myUserHandle(), provider, options);
+ bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUser(), provider, options);
}
/**
@@ -944,7 +944,7 @@
if (mService == null) {
return false;
}
- return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, null);
+ return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, null);
}
/**
@@ -968,7 +968,7 @@
if (mService == null) {
return false;
}
- return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, options);
+ return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, options);
}
/**
@@ -1030,7 +1030,7 @@
return false;
}
try {
- return mService.hasBindAppWidgetPermission(packageName, UserHandle.myUserId());
+ return mService.hasBindAppWidgetPermission(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1050,7 +1050,7 @@
if (mService == null) {
return;
}
- setBindAppWidgetPermission(packageName, UserHandle.myUserId(), permission);
+ setBindAppWidgetPermission(packageName, mContext.getUserId(), permission);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index b255a43..a2e714e 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -262,7 +262,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index faab000..13f0aaf 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -182,7 +182,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 5f0e5d9..e7c8944 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -138,7 +138,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index a2af342..3df4336 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -1507,6 +1507,38 @@
}
/**
+ * Request an LE connection parameter update.
+ *
+ * <p>This function will send an LE connection parameters update request to the remote device.
+ *
+ * @return true, if the request is send to the Bluetooth stack.
+ * @hide
+ */
+ public boolean requestLeConnectionUpdate(int minConnectionInterval,
+ int maxConnectionInterval,
+ int slaveLatency, int supervisionTimeout) {
+ if (DBG) {
+ Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
+ + ")" + (1.25 * minConnectionInterval)
+ + "msec, max=(" + maxConnectionInterval + ")"
+ + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
+ + ", timeout=" + supervisionTimeout + "msec");
+ }
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
+ minConnectionInterval, maxConnectionInterval,
+ slaveLatency, supervisionTimeout);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
*
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 031287f..397b906 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -427,7 +427,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 57a0197..b967fb2 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -491,7 +491,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 2fab305..77ab731 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -236,7 +236,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 8ad0f9d..0ca39f1 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -279,7 +279,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 5b55b23..0fa1d5d 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -109,7 +109,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index af3b662..4f21d93 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -125,7 +125,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 866b063..9f401eb 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -147,7 +147,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 7944354..c60e9e0 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -163,7 +163,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 01b3f6e..1446adc 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -116,7 +116,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 4848162..c51e39a 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -147,7 +147,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ mContext.getUser())) {
Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 09f9684..09a5b59 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -676,6 +676,35 @@
mExcludeSdp = excludeSdp;
}
+ /**
+ * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This
+ * parameter is used by the BT Controller to set the maximum transmission packet size on this
+ * connection. This function is currently used for testing only.
+ * @hide
+ */
+ public void requestMaximumTxDataLength() throws IOException {
+ if (mDevice == null) {
+ throw new IOException("requestMaximumTxDataLength is called on null device");
+ }
+
+ try {
+ if (mSocketState == SocketState.CLOSED) {
+ throw new IOException("socket closed");
+ }
+ IBluetooth bluetoothProxy =
+ BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+ if (bluetoothProxy == null) {
+ throw new IOException("Bluetooth is off");
+ }
+
+ if (DBG) Log.d(TAG, "requestMaximumTxDataLength");
+ bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ throw new IOException("unable to send RPC: " + e.getMessage());
+ }
+ }
+
private String convertAddr(final byte[] addr) {
return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 8d2e141..22496a4 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -166,6 +166,26 @@
public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
/**
+ * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
+ * a foreground app.
+ *
+ * Only the system and the shell user can set it.
+ *
+ * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
+ */
+ public static final String SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC = "force_fg_sync";
+
+ /**
+ * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
+ * a background app.
+ *
+ * Only the system and the shell user can set it.
+ *
+ * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
+ */
+ public static final String SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC = "force_bg_sync";
+
+ /**
* Set by the SyncManager to request that the SyncAdapter initialize itself for
* the given account/authority pair. One required initialization step is to
* ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
@@ -2435,13 +2455,7 @@
public static void addPeriodicSync(Account account, String authority, Bundle extras,
long pollFrequency) {
validateSyncExtrasBundle(extras);
- if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
- || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
- || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
- || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
- || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
- || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
- || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
+ if (invalidPeriodicExtras(extras)) {
throw new IllegalArgumentException("illegal extras were set");
}
try {
@@ -2999,6 +3013,11 @@
}
/** @hide */
+ public int getUserId() {
+ return mContext.getUserId();
+ }
+
+ /** @hide */
public Drawable getTypeDrawable(String mimeType) {
return MimeIconUtils.loadMimeIcon(mContext, mimeType);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f2eb4a0..15f3777 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,6 +49,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -559,6 +560,7 @@
*
* @param resId Resource id for the CharSequence text
*/
+ @NonNull
public final CharSequence getText(@StringRes int resId) {
return getResources().getText(resId);
}
@@ -615,12 +617,11 @@
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return An object that can be used to draw this resource, or
- * {@code null} if the resource could not be resolved.
+ * @return An object that can be used to draw this resource.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final Drawable getDrawable(@DrawableRes int id) {
return getResources().getDrawable(id, getTheme());
}
@@ -632,12 +633,11 @@
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return A color state list, or {@code null} if the resource could not be
- * resolved.
+ * @return A color state list.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final ColorStateList getColorStateList(@ColorRes int id) {
return getResources().getColorStateList(id, getTheme());
}
@@ -3538,6 +3538,7 @@
* @hide
*/
@SystemApi
+ @Deprecated
public static final String WIFI_RTT_SERVICE = "rttmanager";
/**
@@ -4678,9 +4679,15 @@
*
* @hide
*/
- public abstract Context createPackageContextAsUser(
+ @SystemApi
+ public Context createPackageContextAsUser(
String packageName, @CreatePackageOptions int flags, UserHandle user)
- throws PackageManager.NameNotFoundException;
+ throws PackageManager.NameNotFoundException {
+ if (Build.IS_ENG) {
+ throw new IllegalStateException("createPackageContextAsUser not overridden!");
+ }
+ return this;
+ }
/**
* Creates a context given an {@link android.content.pm.ApplicationInfo}.
@@ -4705,13 +4712,22 @@
throws PackageManager.NameNotFoundException;
/**
- * Get the userId associated with this context
- * @return user id
- *
+ * Get the user associated with this context
* @hide
*/
@TestApi
- public abstract @UserIdInt int getUserId();
+ public UserHandle getUser() {
+ return android.os.Process.myUserHandle();
+ }
+
+ /**
+ * Get the user associated with this context
+ * @hide
+ */
+ @TestApi
+ public @UserIdInt int getUserId() {
+ return android.os.UserHandle.myUserId();
+ }
/**
* Return a new Context object for the current Context but whose resources
@@ -4909,6 +4925,19 @@
}
/**
+ * @hide
+ */
+ public boolean isAutofillCompatibilityEnabled() {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+ }
+
+ /**
* Throws an exception if the Context is using system resources,
* which are non-runtime-overlay-themable and may show inconsistent UI.
* @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 8c1293e..a788989 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -994,4 +994,20 @@
public void setAutofillClient(AutofillClient client) {
mBase.setAutofillClient(client);
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean isAutofillCompatibilityEnabled() {
+ return mBase.isAutofillCompatibilityEnabled();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+ mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9b62f19..fa73e3c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -10089,9 +10089,8 @@
}
/** @hide */
- public boolean isBrowsableWebIntent() {
+ public boolean isWebIntent() {
return ACTION_VIEW.equals(mAction)
- && hasCategory(CATEGORY_BROWSABLE)
&& hasWebURI();
}
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 1a207ba..8464e26 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -16,10 +16,14 @@
package android.content.om;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Immutable overlay information about a package. All PackageInfos that
* represent an overlay package will have a corresponding OverlayInfo.
@@ -27,6 +31,19 @@
* @hide
*/
public final class OverlayInfo implements Parcelable {
+
+ @IntDef(prefix = "STATE_", value = {
+ STATE_UNKNOWN,
+ STATE_MISSING_TARGET,
+ STATE_NO_IDMAP,
+ STATE_DISABLED,
+ STATE_ENABLED,
+ STATE_TARGET_UPGRADING,
+ STATE_OVERLAY_UPGRADING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
/**
* An internal state used as the initial state of an overlay. OverlayInfo
* objects exposed outside the {@link
@@ -61,6 +78,18 @@
public static final int STATE_ENABLED = 3;
/**
+ * The target package is currently being upgraded; the state will change
+ * once the package installation has finished.
+ */
+ public static final int STATE_TARGET_UPGRADING = 4;
+
+ /**
+ * The overlay package is currently being upgraded; the state will change
+ * once the package installation has finished.
+ */
+ public static final int STATE_OVERLAY_UPGRADING = 5;
+
+ /**
* Package name of the overlay package
*/
public final String packageName;
@@ -77,13 +106,8 @@
/**
* The state of this OverlayInfo as defined by the STATE_* constants in this class.
- *
- * @see #STATE_MISSING_TARGET
- * @see #STATE_NO_IDMAP
- * @see #STATE_DISABLED
- * @see #STATE_ENABLED
*/
- public final int state;
+ public final @State int state;
/**
* User handle for which this overlay applies
@@ -96,13 +120,13 @@
* @param source the source OverlayInfo to base the new instance on
* @param state the new state for the source OverlayInfo
*/
- public OverlayInfo(@NonNull OverlayInfo source, int state) {
+ public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
this(source.packageName, source.targetPackageName, source.baseCodePath, state,
source.userId);
}
public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
- @NonNull String baseCodePath, int state, int userId) {
+ @NonNull String baseCodePath, @State int state, int userId) {
this.packageName = packageName;
this.targetPackageName = targetPackageName;
this.baseCodePath = baseCodePath;
@@ -136,6 +160,8 @@
case STATE_NO_IDMAP:
case STATE_DISABLED:
case STATE_ENABLED:
+ case STATE_TARGET_UPGRADING:
+ case STATE_OVERLAY_UPGRADING:
break;
default:
throw new IllegalArgumentException("State " + state + " is not a valid state");
@@ -156,7 +182,8 @@
dest.writeInt(userId);
}
- public static final Parcelable.Creator<OverlayInfo> CREATOR = new Parcelable.Creator<OverlayInfo>() {
+ public static final Parcelable.Creator<OverlayInfo> CREATOR =
+ new Parcelable.Creator<OverlayInfo>() {
@Override
public OverlayInfo createFromParcel(Parcel source) {
return new OverlayInfo(source);
@@ -189,14 +216,9 @@
* Translate a state to a human readable string. Only intended for
* debugging purposes.
*
- * @see #STATE_MISSING_TARGET
- * @see #STATE_NO_IDMAP
- * @see #STATE_DISABLED
- * @see #STATE_ENABLED
- *
* @return a human readable String representing the state.
*/
- public static String stateToString(int state) {
+ public static String stateToString(@State int state) {
switch (state) {
case STATE_UNKNOWN:
return "STATE_UNKNOWN";
@@ -208,6 +230,10 @@
return "STATE_DISABLED";
case STATE_ENABLED:
return "STATE_ENABLED";
+ case STATE_TARGET_UPGRADING:
+ return "STATE_TARGET_UPGRADING";
+ case STATE_OVERLAY_UPGRADING:
+ return "STATE_OVERLAY_UPGRADING";
default:
return "<unknown state>";
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index db1630b..1adc9f5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1601,7 +1601,7 @@
* @hide
*/
public boolean isAllowedToUseHiddenApi() {
- return isSystemApp();
+ return isSystemApp() || isUpdatedSystemApp();
}
/**
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 0b16852..8fddb99 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -26,9 +26,12 @@
void addClientProgress(float progress);
String[] getNames();
+
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
+ void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
+
void removeSplit(String splitName);
void close();
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index 38f0225..361d4e4 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -18,12 +18,14 @@
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
/**
* Information needed to make an instant application resolution request.
* @hide
*/
public final class InstantAppRequest {
+
/** Response from the first phase of instant application resolution */
public final AuxiliaryResolveInfo responseObj;
/** The original intent that triggered instant application resolution */
@@ -40,6 +42,8 @@
public final Bundle verificationBundle;
/** Whether resolution occurs because an application is starting */
public final boolean resolveForStart;
+ /** The instant app digest for this request */
+ public final InstantAppResolveInfo.InstantAppDigest digest;
public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
@@ -51,5 +55,11 @@
this.userId = userId;
this.verificationBundle = verificationBundle;
this.resolveForStart = resolveForStart;
+ if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
+ digest = new InstantAppResolveInfo.InstantAppDigest(
+ origIntent.getData().getHost(), 5 /*maxDigests*/);
+ } else {
+ digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
+ }
}
}
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 112c5da..3a95a5f 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -26,10 +26,13 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Random;
/**
* Describes an externally resolvable instant application. There are three states that this class
@@ -227,14 +230,25 @@
*/
@SystemApi
public static final class InstantAppDigest implements Parcelable {
- private static final int DIGEST_MASK = 0xfffff000;
-
+ static final int DIGEST_MASK = 0xfffff000;
public static final InstantAppDigest UNDEFINED =
new InstantAppDigest(new byte[][]{}, new int[]{});
+
+ private static Random sRandom = null;
+ static {
+ try {
+ sRandom = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // oh well
+ sRandom = new Random();
+ }
+ }
/** Full digest of the domain hashes */
private final byte[][] mDigestBytes;
- /** The first 4 bytes of the domain hashes */
+ /** The first 5 bytes of the domain hashes */
private final int[] mDigestPrefix;
+ /** The first 5 bytes of the domain hashes interspersed with random data */
+ private int[] mDigestPrefixSecure;
public InstantAppDigest(@NonNull String hostName) {
this(hostName, -1 /*maxDigests*/);
@@ -306,6 +320,7 @@
}
}
mDigestPrefix = in.createIntArray();
+ mDigestPrefixSecure = in.createIntArray();
}
public byte[][] getDigestBytes() {
@@ -316,6 +331,26 @@
return mDigestPrefix;
}
+ /**
+ * Returns a digest prefix with additional random prefixes interspersed.
+ * @hide
+ */
+ public int[] getDigestPrefixSecure() {
+ if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
+ return getDigestPrefix();
+ } else if (mDigestPrefixSecure == null) {
+ // let's generate some random data to intersperse throughout the set of prefixes
+ final int realSize = getDigestPrefix().length;
+ final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
+ mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
+ for (int i = realSize; i < manufacturedSize; i++) {
+ mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
+ }
+ Arrays.sort(mDigestPrefixSecure);
+ }
+ return mDigestPrefixSecure;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -323,6 +358,11 @@
@Override
public void writeToParcel(Parcel out, int flags) {
+ final boolean isUndefined = this == UNDEFINED;
+ out.writeBoolean(isUndefined);
+ if (isUndefined) {
+ return;
+ }
if (mDigestBytes == null) {
out.writeInt(-1);
} else {
@@ -332,6 +372,7 @@
}
}
out.writeIntArray(mDigestPrefix);
+ out.writeIntArray(mDigestPrefixSecure);
}
@SuppressWarnings("hiding")
@@ -339,6 +380,9 @@
new Parcelable.Creator<InstantAppDigest>() {
@Override
public InstantAppDigest createFromParcel(Parcel in) {
+ if (in.readBoolean() /* is undefined */) {
+ return UNDEFINED;
+ }
return new InstantAppDigest(in);
}
@Override
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 09a46b8..0342c93 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -468,6 +468,18 @@
dest.writeBoolean(mOverlayIsStatic);
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
+ writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
+ }
+
+ private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
+ if (signingCertificateHistory != null) {
+ dest.writeInt(signingCertificateHistory.length);
+ for (int i = 0; i < signingCertificateHistory.length; i++) {
+ dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
+ }
+ } else {
+ dest.writeInt(-1);
+ }
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -523,6 +535,7 @@
mOverlayIsStatic = source.readBoolean();
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
+ readSigningCertificateHistoryFromParcel(source);
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
@@ -534,6 +547,16 @@
}
}
+ private void readSigningCertificateHistoryFromParcel(Parcel source) {
+ int len = source.readInt();
+ if (len != -1) {
+ signingCertificateHistory = new Signature[len][];
+ for (int i = 0; i < len; i++) {
+ signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
+ }
+ }
+ }
+
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
if (components != null) {
for (ComponentInfo ci : components) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df677d2..d0be6c8 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -829,7 +829,19 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ }
+ /** {@hide} */
+ public void write(@NonNull String name, long offsetBytes, long lengthBytes,
+ @NonNull ParcelFileDescriptor fd) throws IOException {
+ try {
+ mSession.write(name, offsetBytes, lengthBytes, fd);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0b4231f..486c86c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -441,6 +441,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final int MATCH_FACTORY_ONLY = 0x00200000;
/**
@@ -2965,6 +2966,11 @@
*/
public static final int VERSION_CODE_HIGHEST = -1;
+ /** {@hide} */
+ public int getUserId() {
+ return UserHandle.myUserId();
+ }
+
/**
* Retrieve overall information about an application package that is
* installed on the system.
@@ -5212,7 +5218,7 @@
*/
@Deprecated
public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
- getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
+ getPackageSizeInfoAsUser(packageName, getUserId(), observer);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2da8937..377942a 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.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;
@@ -196,10 +197,6 @@
private static final String TAG_RESTRICT_UPDATE = "restrict-update";
private static final String TAG_USES_SPLIT = "uses-split";
- // [b/36551762] STOPSHIP remove the ability to expose components via meta-data
- // Temporary workaround; allow meta-data to expose components to instant apps
- private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed";
-
private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
/**
@@ -1290,7 +1287,6 @@
*/
@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) {
@@ -1299,8 +1295,9 @@
}
}
+ 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;
@@ -1308,28 +1305,10 @@
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
- IoUtils.closeQuietly(assets);
+ IoUtils.closeQuietly(assetLoader);
}
}
- 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);
- }
- return cookie;
- }
-
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
@@ -1345,13 +1324,15 @@
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);
@@ -1386,15 +1367,18 @@
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);
@@ -1596,21 +1580,19 @@
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) {
@@ -1637,7 +1619,7 @@
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
- IoUtils.closeQuietly(assets);
+ // TODO(b/72056911): Implement and call close() on ApkAssets.
}
}
@@ -4430,24 +4412,6 @@
outError)) == null) {
return null;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && a.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- a.info.flags &= ~ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = a.intents.size() - 1; i >= 0; --i) {
- a.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- if (owner.preferredActivityFilters != null) {
- for (int i = owner.preferredActivityFilters.size() - 1; i >= 0; --i) {
- owner.preferredActivityFilters.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
- }
} else if (!receiver && parser.getName().equals("layout")) {
parseLayout(res, parser, a);
} else {
@@ -4942,7 +4906,7 @@
p.info.authority = cpname.intern();
if (!parseProviderTags(
- res, parser, visibleToEphemeral, owner, p, outError)) {
+ res, parser, visibleToEphemeral, p, outError)) {
return null;
}
@@ -4950,7 +4914,7 @@
}
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
- boolean visibleToEphemeral, Package owner, Provider outInfo, String[] outError)
+ boolean visibleToEphemeral, Provider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -4978,17 +4942,6 @@
outInfo.metaData, outError)) == null) {
return false;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && outInfo.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = outInfo.intents.size() - 1; i >= 0; --i) {
- outInfo.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
} else if (parser.getName().equals("grant-uri-permission")) {
TypedArray sa = res.obtainAttributes(parser,
@@ -5277,17 +5230,6 @@
outError)) == null) {
return null;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && s.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = s.intents.size() - 1; i >= 0; --i) {
- s.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <service>: "
@@ -6738,31 +6680,6 @@
+ " " + packageName + "}";
}
- public String dumpState_temp() {
- String flags = "";
- flags += ((applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
- flags += ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
- if ("".equals(flags)) {
- flags = "-";
- }
- String privFlags = "";
- privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
- privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
- privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
- if ("".equals(privFlags)) {
- privFlags = "-";
- }
- return "Package{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + packageName
- + ", ver:" + getLongVersionCode()
- + ", path: " + codePath
- + ", flags: " + flags
- + ", privFlags: " + privFlags
- + ", extra: " + (mExtras == null ? "<<NULL>>" : Integer.toHexString(System.identityHashCode(mExtras)) + "}")
- + "}";
- }
-
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 3f63d80..fc2eba2 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -277,7 +277,7 @@
dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
}
if (dr != null) {
- return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId()));
+ return pm.getUserBadgedIcon(dr, new UserHandle(pm.getUserId()));
}
return ci.loadIcon(pm);
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 30222b7..25e0ccd 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -770,6 +770,6 @@
/** @hide injection point */
@VisibleForTesting
protected int injectMyUserId() {
- return UserHandle.myUserId();
+ return mContext.getUserId();
}
}
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6a..90be6f31 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Random;
@@ -86,6 +88,7 @@
* @return verifier device identity based on the input from the provided
* random number generator
*/
+ @VisibleForTesting
static VerifierDeviceIdentity generate(Random rng) {
long identity = rng.nextLong();
return new VerifierDeviceIdentity(identity);
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 02d0a6d..79bc9a3 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -274,6 +274,7 @@
}
}
+ @GuardedBy("mLock")
private void scheduleNextMessageIfNeededLocked() {
if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
Message nextMessage = mPendingWork.remove(0);
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 99eb470..9e3a8f4 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 libcore.io.IoUtils;
+import java.io.IOException;
+
/**
* Loads the base and split APKs into a single AssetManager.
* @hide
@@ -33,68 +38,66 @@
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);
+ ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+ ? mSplitCodePaths.length : 0) + 1];
- if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
- for (String apkPath : mSplitCodePaths) {
- loadApkIntoAssetManager(assets, apkPath, mFlags);
- }
- }
+ // Load the base.
+ int splitIdx = 0;
+ apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
- mCachedAssetManager = assets;
- assets = null;
- return mCachedAssetManager;
- } finally {
- if (assets != null) {
- IoUtils.closeQuietly(assets);
+ // 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 16023f0..58eaabf 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 @@
* is to be used when an application opts-in to isolated split loading.
* @hide
*/
-public class SplitAssetDependencyLoader
- extends SplitDependencyLoader<PackageParser.PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
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<int[]> dependencies, int flags) {
+ SparseArray<int[]> 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 @@
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 @@
return mCachedAssetManagers[splitIdx] != null;
}
- private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
- throws PackageParser.PackageParserException {
- final 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);
-
- 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;
+ 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 {
+ 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<String> assetPaths = new ArrayList<>();
+ int parentSplitIdx) throws PackageParserException {
+ final ArrayList<ApkAssets> 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 0000000..fd664bc
--- /dev/null
+++ b/core/java/android/content/res/ApkAssets.java
@@ -0,0 +1,190 @@
+/*
+ * 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*/);
+ }
+
+ @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
+ * <em>NOT</em> 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
+ 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 7866560..bb90700 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -18,9 +18,11 @@
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;
@@ -28,10 +30,20 @@
import android.util.SparseArray;
import android.util.TypedValue;
-import java.io.FileDescriptor;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+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;
/**
@@ -42,7 +54,19 @@
* 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];
/**
* Mode for {@link #open(String, int)}: no specific information about how
@@ -65,146 +89,301 @@
*/
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;
- private StringBlock mStringBlocks[] = null;
-
- private int mNumRefs = 1;
- private boolean mOpen = true;
- private HashMap<Long, RuntimeException> mRefStacks;
-
+ // 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<Long, RuntimeException> 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;
+ }
+
+ mObject = nativeCreate();
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
+ }
+
+ // Always set the framework resources.
+ setApkAssets(assets, false /*invalidateCaches*/);
+ }
+
+ /**
+ * 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) {
+ mNumRefs = 0;
+ incRefsLocked(hashCode());
}
}
- private static void ensureSystemAssets() {
- synchronized (sSync) {
- if (sSystem == null) {
- AssetManager system = new AssetManager(true);
- system.makeStringBlocks(null);
- sSystem = system;
- }
+ /**
+ * 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 {
+ ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+ apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
+ loadStaticRuntimeOverlays(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);
}
}
-
- private AssetManager(boolean isSystem) {
- if (DEBUG_REFS) {
- synchronized (this) {
- mNumRefs = 0;
- incRefsLocked(this.hashCode());
- }
+
+ /**
+ * 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<ApkAssets> 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;
}
- init(true);
- if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+
+ 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);
+ }
}
/**
* 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");
+
+ // Copy the apkAssets, but prepend the system assets (framework + overlays).
+ final ApkAssets[] newApkAssets = new ApkAssets[apkAssets.length + sSystemApkAssets.length];
+ System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
+ System.arraycopy(apkAssets, 0, newApkAssets, sSystemApkAssets.length, apkAssets.length);
+
+ 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;
}
/**
- * Retrieves the string value associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier to load
- * @return the string value, or {@code null}
+ * 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
*/
- @Nullable
- final CharSequence getResourceText(@StringRes int resId) {
+ public int findCookieForPath(@NonNull String path) {
+ Preconditions.checkNotNull(path, "path");
synchronized (this) {
- final TypedValue outValue = mValue;
- if (getResourceValue(resId, 0, outValue, true)) {
- return outValue.coerceToString();
+ ensureValidLocked();
+ final int count = mApkAssets.length;
+ for (int i = 0; i < count; i++) {
+ if (path.equals(mApkAssets[i].getAssetPath())) {
+ return i + 1;
+ }
}
- return null;
}
+ return 0;
}
/**
- * Retrieves the string value associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier to load
- * @param bagEntryId
- * @return the string value, or {@code null}
+ * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+ * @hide
*/
- @Nullable
- final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ @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) {
- final TypedValue outValue = mValue;
- final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
- if (block < 0) {
- return null;
+ ensureOpenLocked();
+ final int count = mApkAssets.length;
+ for (int i = 0; i < count; i++) {
+ if (mApkAssets[i].getAssetPath().equals(path)) {
+ return i + 1;
+ }
}
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- return mStringBlocks[block].get(outValue.data);
+ 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;
}
- return outValue.coerceToString();
+
+ final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1);
+ newApkAssets[count] = assets;
+ setApkAssets(newApkAssets, true);
+ return count + 1;
}
}
/**
- * Retrieves the string array associated with a particular resource
- * identifier for the current configuration.
- *
- * @param resId the resource identifier of the string array
- * @return the string array, or {@code null}
+ * 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.
*/
- @Nullable
- final String[] getResourceStringArray(@ArrayRes int resId) {
- return getArrayStringResource(resId);
+ @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");
+ }
}
/**
@@ -219,11 +398,14 @@
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+ boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
+ Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
- final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
- if (block < 0) {
+ ensureValidLocked();
+ final int cookie = nativeGetResourceValue(
+ mObject, resId, (short) densityDpi, outValue, resolveRefs);
+ if (cookie <= 0) {
return false;
}
@@ -232,38 +414,156 @@
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mStringBlocks[block].get(outValue.data);
+ outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
}
return true;
}
}
/**
+ * Retrieves the string value associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @return the string value, or {@code null}
+ */
+ @Nullable CharSequence getResourceText(@StringRes int resId) {
+ synchronized (this) {
+ final TypedValue outValue = mValue;
+ if (getResourceValue(resId, 0, outValue, true)) {
+ return outValue.coerceToString();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the string value associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @param bagEntryId the index into the bag to load
+ * @return the string value, or {@code null}
+ */
+ @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ synchronized (this) {
+ ensureValidLocked();
+ final TypedValue outValue = mValue;
+ final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
+ if (cookie <= 0) {
+ return null;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ }
+ return outValue.coerceToString();
+ }
+ }
+
+ int getResourceArraySize(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArraySize(mObject, resId);
+ }
+ }
+
+ /**
+ * Populates `outData` with array elements of `resId`. `outData` is normally
+ * used with
+ * {@link TypedArray}.
+ *
+ * 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
+ */
+ int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
+ Preconditions.checkNotNull(outData, "outData");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceArray(mObject, resId, outData);
+ }
+ }
+
+ /**
+ * Retrieves the string array associated with a particular resource
+ * identifier for the current configuration.
+ *
+ * @param resId the resource identifier of the string array
+ * @return the string array, or {@code null}
+ */
+ @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceStringArray(mObject, resId);
+ }
+ }
+
+ /**
* Retrieve the text array associated with a particular resource
* identifier.
*
* @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
@@ -277,73 +577,88 @@
* @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;
- }
-
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- final StringBlock[] blocks = ensureStringBlocks();
- outValue.string = blocks[block].get(outValue.data);
- }
- return true;
- }
-
- /**
- * Ensures the string blocks are loaded.
- *
- * @return the string blocks
- */
- @NonNull
- final StringBlock[] ensureStringBlocks() {
+ Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
- if (mStringBlocks == null) {
- makeStringBlocks(sSystem.mStringBlocks);
+ ensureValidLocked();
+ final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
+ resolveRefs);
+ if (cookie <= 0) {
+ return false;
}
- return mStringBlocks;
+
+ // 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;
}
}
- /*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<num; i++) {
- if (i < seedNum) {
- mStringBlocks[i] = seed[i];
- } else {
- mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
- }
- }
- }
-
- /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
+ void dumpTheme(long theme, int priority, String tag, String prefix) {
synchronized (this) {
- // Cookies map to string blocks starting at 1.
- return mStringBlocks[cookie - 1].get(id);
+ ensureValidLocked();
+ nativeThemeDump(mObject, theme, priority, tag, prefix);
}
}
+ @Nullable String getResourceName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourcePackageName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourcePackageName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourceTypeName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceTypeName(mObject, resId);
+ }
+ }
+
+ @Nullable String getResourceEntryName(@AnyRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetResourceEntryName(mObject, resId);
+ }
+ }
+
+ @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
+ @Nullable String defPackage) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
+ }
+ }
+
+ CharSequence getPooledStringForCookie(int cookie, int id) {
+ // Cookies map to ApkAssets starting at 1.
+ return getApkAssets()[cookie - 1].getStringFromPool(id);
+ }
+
/**
* Open an asset using ACCESS_STREAMING mode. This provides access to
* files that have been bundled with an application as assets -- that is,
* files placed in to the "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
*
* @see #open(String, int)
* @see #list
*/
- public final InputStream open(String fileName) throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName) throws IOException {
return open(fileName, ACCESS_STREAMING);
}
@@ -353,8 +668,7 @@
* with an application as assets -- that is, files placed in to the
* "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be
- * hierarchical.
+ * @param fileName The name of the asset to open. This name can be hierarchical.
* @param accessMode Desired access mode for retrieving the data.
*
* @see #ACCESS_UNKNOWN
@@ -364,34 +678,40 @@
* @see #open(String)
* @see #list
*/
- public final InputStream open(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long asset = nativeOpenAsset(mObject, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- long asset = openAsset(fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
- public final AssetFileDescriptor openFd(String fileName)
- throws IOException {
+ /**
+ * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides access to files that have been bundled with an application as assets -- that
+ * is, files placed in to the "assets" directory.
+ *
+ * The asset must be uncompressed, or an exception will be thrown.
+ *
+ * @param fileName The name of the asset to open. This name can be hierarchical.
+ * @return An open AssetFileDescriptor.
+ */
+ public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
- }
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset file: " + fileName);
}
/**
@@ -406,90 +726,121 @@
*
* @see #open
*/
- public native final String[] list(String path)
- throws IOException;
+ public @Nullable String[] list(@NonNull String path) throws IOException {
+ Preconditions.checkNotNull(path, "path");
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeList(mObject, path);
+ }
+ }
/**
- * {@hide}
* Open a non-asset file as an asset using ACCESS_STREAMING mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ *
* @see #open(String)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName) throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
return openNonAsset(0, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset file as an asset using a specific access mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
+ *
+ * @param fileName Name of the asset to retrieve.
+ * @param accessMode Desired access mode for retrieving the data.
+ *
+ * @see #ACCESS_UNKNOWN
+ * @see #ACCESS_STREAMING
+ * @see #ACCESS_RANDOM
+ * @see #ACCESS_BUFFER
* @see #open(String, int)
+ * @hide
*/
- public final InputStream openNonAsset(String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
+ throws IOException {
return openNonAsset(0, fileName, accessMode);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
+ throws IOException {
return openNonAsset(cookie, fileName, ACCESS_STREAMING);
}
/**
- * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
* @param accessMode Desired access mode for retrieving the data.
+ * @hide
*/
- public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
- throws IOException {
+ public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
+ if (asset == 0) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- long asset = openNonAssetNative(cookie, fileName, accessMode);
- if (asset != 0) {
- AssetInputStream res = new AssetInputStream(asset);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final AssetInputStream assetInputStream = new AssetInputStream(asset);
+ incRefsLocked(assetInputStream.hashCode());
+ return assetInputStream;
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- public final AssetFileDescriptor openNonAssetFd(String fileName)
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
throws IOException {
return openNonAssetFd(0, fileName);
}
-
- public final AssetFileDescriptor openNonAssetFd(int cookie,
- String fileName) throws IOException {
+
+ /**
+ * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+ * This provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use this.
+ *
+ * The asset must not be compressed, or an exception will be thrown.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ */
+ public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
+ throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final ParcelFileDescriptor pfd =
+ nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
+ if (pfd == null) {
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
- fileName, mOffsets);
- if (pfd != null) {
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
- }
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
}
- throw new FileNotFoundException("Asset absolute file: " + fileName);
}
/**
@@ -497,7 +848,7 @@
*
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(String fileName)
+ public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
throws IOException {
return openXmlResourceParser(0, fileName);
}
@@ -508,270 +859,265 @@
* @param cookie Identifier of the package to be opened.
* @param fileName The name of the file to retrieve.
*/
- public final XmlResourceParser openXmlResourceParser(int cookie,
- String fileName) throws IOException {
- XmlBlock block = openXmlBlockAsset(cookie, fileName);
- XmlResourceParser rp = block.newParser();
- block.close();
- return rp;
+ public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
+ throws IOException {
+ try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
+ XmlResourceParser parser = block.newParser();
+ // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
+ // 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;
+ }
}
/**
- * {@hide}
- * Retrieve a non-asset as a compiled XML file. Not for use by
- * applications.
+ * Retrieve a non-asset as a compiled XML file. Not for use by applications.
*
* @param fileName The name of the file to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
return openXmlBlockAsset(0, fileName);
}
/**
- * {@hide}
* Retrieve a non-asset as a compiled XML file. Not for use by
* applications.
*
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
+ * @hide
*/
- /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
- throws IOException {
+ @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
+ Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
+ ensureOpenLocked();
+ final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+ if (xmlBlock == 0) {
+ throw new FileNotFoundException("Asset XML file: " + fileName);
}
- long xmlBlock = openXmlAssetNative(cookie, fileName);
- if (xmlBlock != 0) {
- XmlBlock res = new XmlBlock(this, xmlBlock);
- incRefsLocked(res.hashCode());
- return res;
- }
+ final XmlBlock block = new XmlBlock(this, xmlBlock);
+ incRefsLocked(block.hashCode());
+ return block;
}
- throw new FileNotFoundException("Asset XML file: " + fileName);
}
- /*package*/ void xmlBlockGone(int id) {
+ void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
}
}
- /*package*/ final long createTheme() {
+ void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+ long outIndicesAddress) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
synchronized (this) {
- if (!mOpen) {
- throw new RuntimeException("Assetmanager has been closed");
- }
- long res = newTheme();
- incRefsLocked(res);
- return res;
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
+ outIndicesAddress);
}
}
- /*package*/ final void releaseTheme(long theme) {
+ boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+ @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
+ @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
synchronized (this) {
- deleteTheme(theme);
- decRefsLocked(theme);
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeResolveAttrs(mObject,
+ themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
}
}
+ boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
+ @NonNull int[] outValues, @NonNull int[] outIndices) {
+ Preconditions.checkNotNull(parser, "parser");
+ Preconditions.checkNotNull(inAttrs, "inAttrs");
+ Preconditions.checkNotNull(outValues, "outValues");
+ Preconditions.checkNotNull(outIndices, "outIndices");
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ return nativeRetrieveAttributes(
+ mObject, parser.mParseState, inAttrs, outValues, outIndices);
+ }
+ }
+
+ long createTheme() {
+ synchronized (this) {
+ ensureValidLocked();
+ long themePtr = nativeThemeCreate(mObject);
+ incRefsLocked(themePtr);
+ return themePtr;
+ }
+ }
+
+ void releaseTheme(long themePtr) {
+ synchronized (this) {
+ nativeThemeDestroy(themePtr);
+ decRefsLocked(themePtr);
+ }
+ }
+
+ void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
+ synchronized (this) {
+ // Need to synchronize on AssetManager because we will be accessing
+ // the native implementation of AssetManager.
+ ensureValidLocked();
+ nativeThemeApplyStyle(mObject, themePtr, resId, force);
+ }
+ }
+
+ @Override
protected void finalize() throws Throwable {
- try {
- if (DEBUG_REFS && mNumRefs != 0) {
- Log.w(TAG, "AssetManager " + this
- + " finalized with non-zero refs: " + mNumRefs);
- if (mRefStacks != null) {
- for (RuntimeException e : mRefStacks.values()) {
- Log.w(TAG, "Reference from here", e);
- }
+ if (DEBUG_REFS && mNumRefs != 0) {
+ Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+ if (mRefStacks != null) {
+ for (RuntimeException e : mRefStacks.values()) {
+ Log.w(TAG, "Reference from here", e);
}
}
- destroy();
- } finally {
- super.finalize();
+ }
+
+ if (mObject != 0) {
+ nativeDestroy(mObject);
}
}
-
+
+ /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
+ safe and it does not rely on AssetManager once it has been created. It completely owns the
+ underlying Asset. */
public final class AssetInputStream extends InputStream {
+ private long mAssetNativePtr;
+ private long mLength;
+ private long mMarkPos;
+
/**
* @hide
*/
public final int getAssetInt() {
throw new UnsupportedOperationException();
}
+
/**
* @hide
*/
public final long getNativeAsset() {
- return mAsset;
+ return mAssetNativePtr;
}
- private AssetInputStream(long asset)
- {
- mAsset = asset;
- mLength = getAssetLength(asset);
+
+ private AssetInputStream(long assetNativePtr) {
+ mAssetNativePtr = assetNativePtr;
+ mLength = nativeAssetGetLength(assetNativePtr);
}
+
+ @Override
public final int read() throws IOException {
- return readAssetChar(mAsset);
+ ensureOpen();
+ return nativeAssetReadChar(mAssetNativePtr);
}
- public final boolean markSupported() {
- return true;
+
+ @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 available() throws IOException {
- long len = getAssetRemainingLength(mAsset);
- return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)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);
}
- 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);
- }
- 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);
- }
- public final int read(byte[] b, int off, int len) throws IOException {
- return readAsset(mAsset, 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
- {
+ @Override
+ public final int available() throws IOException {
+ ensureOpen();
+ final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
+ return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
+ }
+
+ @Override
+ public final boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public final void mark(int readlimit) {
+ ensureOpen();
+ mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ }
+
+ @Override
+ public final void reset() throws IOException {
+ ensureOpen();
+ nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
+ }
+
+ @Override
+ public final void close() throws IOException {
+ if (mAssetNativePtr != 0) {
+ nativeAssetDestroy(mAssetNativePtr);
+ mAssetNativePtr = 0;
+
+ synchronized (AssetManager.this) {
+ decRefsLocked(hashCode());
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
close();
}
- 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;
+ private void ensureOpen() {
+ if (mAssetNativePtr == 0) {
+ throw new IllegalStateException("AssetInputStream is closed");
+ }
}
}
- 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;
- }
- }
-
- 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;
- }
- }
-
- /**
- * See addOverlayPath.
- *
- * {@hide}
- */
- public native final int addOverlayPathNative(String idmapPath);
-
- /**
- * 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;
- }
-
- int[] cookies = new int[paths.length];
- for (int i = 0; i < paths.length; i++) {
- cookies[i] = addAssetPath(paths[i]);
- }
-
- return cookies;
- }
-
/**
* 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.
@@ -784,7 +1130,12 @@
* 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
@@ -794,131 +1145,58 @@
* 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
+ * @hide
+ */
+ Configuration[] getSizeConfigurations() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetSizeConfigurations(mObject);
+ }
+ }
+
+ /**
+ * Change the configuration 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);
+ 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);
+ }
+ }
/**
- * Retrieve the resource identifier for the given resource name.
+ * @hide
*/
- /*package*/ native final int getResourceIdentifier(String name,
- String defType,
- String defPackage);
+ public SparseArray<String> getAssignedPackageIdentifiers() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetAssignedPackageIdentifiers(mObject);
+ }
+ }
- /*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<String> getAssignedPackageIdentifiers();
-
- /**
- * {@hide}
- */
- public native static final int getGlobalAssetCount();
-
- /**
- * {@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();
-
- private final void incRefsLocked(long id) {
+ @GuardedBy("this")
+ private void incRefsLocked(long id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
- mRefStacks = new HashMap<Long, RuntimeException>();
+ mRefStacks = new HashMap<>();
}
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
@@ -926,16 +1204,119 @@
}
mNumRefs++;
}
-
- private final void decRefsLocked(long id) {
+
+ @GuardedBy("this")
+ 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<String> 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/Resources.java b/core/java/android/content/res/Resources.java
index e173653c..d813382 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -590,7 +590,7 @@
*/
@NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
+ int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
if (res != null) {
return res;
}
@@ -613,13 +613,13 @@
@NonNull
public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
final ResourcesImpl impl = mResourcesImpl;
- int len = impl.getAssets().getArraySize(id);
+ int len = impl.getAssets().getResourceArraySize(id);
if (len < 0) {
throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = impl.getAssets().retrieveArray(id, array.mData);
+ array.mLength = impl.getAssets().getResourceArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -867,8 +867,9 @@
* @param theme The theme used to style the drawable attributes, may be {@code null}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
- * not exist.
+ * not exist, or cannot be decoded.
*/
+ @NonNull
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
final TypedValue value = obtainTempTypedValue();
try {
@@ -980,7 +981,7 @@
* or multiple colors that can be selected based on a state.
* @deprecated Use {@link #getColorStateList(int, Theme)} instead.
*/
- @Nullable
+ @NonNull
@Deprecated
public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
final ColorStateList csl = getColorStateList(id, null);
@@ -1011,7 +1012,7 @@
* @return A themed ColorStateList object containing either a single solid
* color or multiple colors that can be selected based on a state.
*/
- @Nullable
+ @NonNull
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
@@ -1024,7 +1025,7 @@
}
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
throws NotFoundException {
return mResourcesImpl.loadColorStateList(this, value, id, theme);
@@ -1033,7 +1034,7 @@
/**
* @hide
*/
- @Nullable
+ @NonNull
public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
return mResourcesImpl.loadComplexColor(this, value, id, theme);
}
@@ -1139,6 +1140,7 @@
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
@@ -1163,6 +1165,7 @@
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "anim");
}
@@ -1188,6 +1191,7 @@
*
* @see android.util.AttributeSet
*/
+ @NonNull
public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "xml");
}
@@ -1203,8 +1207,8 @@
* @return InputStream Access to the resource data.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
- *
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
@@ -1261,6 +1265,7 @@
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id, TypedValue value)
throws NotFoundException {
return mResourcesImpl.openRawResource(id, value);
@@ -1789,8 +1794,7 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
- array.mData, array.mIndices);
+ mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
array.mXml = parser;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 97cb78b..08a1613 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -168,7 +168,6 @@
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
- mAssets.ensureStringBlocks();
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -543,7 +542,7 @@
}
}
- @Nullable
+ @NonNull
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
throws NotFoundException {
@@ -627,7 +626,7 @@
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
- dr = loadDrawableForCookie(wrapper, value, id, density, null);
+ dr = loadDrawableForCookie(wrapper, value, id, density);
}
// DrawableContainer' constant state has drawables instances. In order to leave the
// constant state intact in the cache, we need to create a new DrawableContainer after
@@ -754,8 +753,9 @@
/**
* Loads a drawable from XML or resources stream.
*/
+ @NonNull
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
- int id, int density, @Nullable Resources.Theme theme) {
+ int id, int density) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -774,22 +774,23 @@
}
}
- // For prelaod tracing.
+ // For preload tracing.
long startTime = 0;
int startBitmapCount = 0;
long startBitmapSize = 0;
- int startDrwableCount = 0;
+ int startDrawableCount = 0;
if (TRACE_FOR_DETAILED_PRELOAD) {
startTime = System.nanoTime();
startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
- startDrwableCount = sPreloadTracingNumLoadedDrawables;
+ startDrawableCount = sPreloadTracingNumLoadedDrawables;
}
if (DEBUG_LOAD) {
Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
}
+
final Drawable dr;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
@@ -804,7 +805,7 @@
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
+ dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(
@@ -834,7 +835,7 @@
final long loadedBitmapSize =
Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
final int loadedDrawables =
- sPreloadTracingNumLoadedDrawables - startDrwableCount;
+ sPreloadTracingNumLoadedDrawables - startDrawableCount;
sPreloadTracingNumLoadedDrawables++;
@@ -910,6 +911,7 @@
* first try to load CSL from the cache. If not found, try to get from the constant state.
* Last, parse the XML and generate the CSL.
*/
+ @NonNull
private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
TypedValue value, int id) {
final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -929,17 +931,15 @@
complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
}
- if (complexColor != null) {
- complexColor.setBaseChangingConfigurations(value.changingConfigurations);
+ complexColor.setBaseChangingConfigurations(value.changingConfigurations);
- if (mPreloading) {
- if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
- 0, value.resourceId, "color")) {
- sPreloadedComplexColors.put(key, complexColor.getConstantState());
- }
- } else {
- cache.put(key, theme, complexColor.getConstantState());
+ if (mPreloading) {
+ if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
+ 0, value.resourceId, "color")) {
+ sPreloadedComplexColors.put(key, complexColor.getConstantState());
}
+ } else {
+ cache.put(key, theme, complexColor.getConstantState());
}
return complexColor;
}
@@ -985,7 +985,7 @@
return complexColor;
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
Resources.Theme theme)
throws NotFoundException {
@@ -1045,7 +1045,7 @@
*
* @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
*/
- @Nullable
+ @NonNull
private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
Resources.Theme theme) {
if (value.string == null) {
@@ -1274,8 +1274,7 @@
void applyStyle(int resId, boolean force) {
synchronized (mKey) {
- AssetManager.applyThemeStyle(mTheme, resId, force);
-
+ mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
mKey.append(resId, force);
}
@@ -1284,7 +1283,7 @@
void setTo(ThemeImpl other) {
synchronized (mKey) {
synchronized (other.mKey) {
- AssetManager.copyTheme(mTheme, other.mTheme);
+ AssetManager.nativeThemeCopy(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
mKey.setTo(other.getKey());
@@ -1307,12 +1306,10 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0,
- attrs, attrs.length, array.mDataAddress, array.mIndicesAddress);
+ mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
+ array.mDataAddress, array.mIndicesAddress);
array.mTheme = wrapper;
array.mXml = parser;
-
return array;
}
}
@@ -1329,7 +1326,7 @@
}
final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
- AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
array.mTheme = wrapper;
array.mXml = null;
return array;
@@ -1349,14 +1346,14 @@
@Config int getChangingConfigurations() {
synchronized (mKey) {
final @NativeConfig int nativeChangingConfig =
- AssetManager.getThemeChangingConfigurations(mTheme);
+ AssetManager.nativeThemeGetChangingConfigurations(mTheme);
return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
}
}
public void dump(int priority, String tag, String prefix) {
synchronized (mKey) {
- AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ mAssets.dumpTheme(mTheme, priority, tag, prefix);
}
}
@@ -1385,13 +1382,13 @@
*/
void rebase() {
synchronized (mKey) {
- AssetManager.clearTheme(mTheme);
+ AssetManager.nativeThemeClear(mTheme);
// Reapply the same styles in the same order.
for (int i = 0; i < mKey.mCount; i++) {
final int resId = mKey.mResId[i];
final boolean force = mKey.mForce[i];
- AssetManager.applyThemeStyle(mTheme, resId, force);
+ mAssets.applyStyleToTheme(mTheme, resId, force);
}
}
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f33c751..cbb3c6d 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -61,6 +61,15 @@
return attrs;
}
+ // STYLE_ prefixed constants are offsets within the typed data array.
+ static final int STYLE_NUM_ENTRIES = 6;
+ static final int STYLE_TYPE = 0;
+ static final int STYLE_DATA = 1;
+ static final int STYLE_ASSET_COOKIE = 2;
+ static final int STYLE_RESOURCE_ID = 3;
+ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+ static final int STYLE_DENSITY = 5;
+
private final Resources mResources;
private DisplayMetrics mMetrics;
private AssetManager mAssets;
@@ -78,7 +87,7 @@
private void resize(int len) {
mLength = len;
- final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES;
+ final int dataLen = len * STYLE_NUM_ENTRIES;
final int indicesLen = len + 1;
final VMRuntime runtime = VMRuntime.getRuntime();
if (mDataAddress == 0 || mData.length < dataLen) {
@@ -166,9 +175,9 @@
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 null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -203,9 +212,9 @@
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 null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -242,14 +251,13 @@
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_STRING) {
- final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ final int cookie = data[index + STYLE_ASSET_COOKIE];
if (cookie < 0) {
- return mXml.getPooledString(
- data[index+AssetManager.STYLE_DATA]).toString();
+ return mXml.getPooledString(data[index + STYLE_DATA]).toString();
}
}
return null;
@@ -274,11 +282,11 @@
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];
final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + STYLE_CHANGING_CONFIGURATIONS]);
if ((changingConfigs & ~allowedChangingConfigs) != 0) {
return null;
}
@@ -320,14 +328,14 @@
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] != 0;
+ return data[index + STYLE_DATA] != 0;
}
final TypedValue v = mValue;
@@ -359,14 +367,14 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 e6b95741..d4ccffb 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 @@
*
* {@hide}
*/
-final class XmlBlock {
+final class XmlBlock implements AutoCloseable {
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
@@ -48,6 +49,7 @@
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
+ @Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -478,13 +480,13 @@
* 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/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index fdb702f..76fa008 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -395,7 +395,7 @@
*/
@Override
public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
- setNotificationUri(cr, notifyUri, UserHandle.myUserId());
+ setNotificationUri(cr, notifyUri, cr.getUserId());
}
/** @hide - set the notification uri but with an observer for a particular user's view */
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index b211700..dc60612 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -422,6 +422,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private boolean recycleConnectionLocked(SQLiteConnection connection,
AcquiredConnectionStatus status) {
if (status == AcquiredConnectionStatus.RECONFIGURE) {
@@ -531,6 +532,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeAvailableConnectionsAndLogExceptionsLocked() {
closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
@@ -541,6 +543,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private boolean closeAvailableConnectionLocked(int connectionId) {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = count - 1; i >= 0; i--) {
@@ -562,6 +565,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = 0; i < count; i++) {
@@ -581,6 +585,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeExcessConnectionsAndLogExceptionsLocked() {
int availableCount = mAvailableNonPrimaryConnections.size();
while (availableCount-- > mMaxConnectionPoolSize - 1) {
@@ -591,6 +596,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
try {
connection.close(); // might throw
@@ -609,6 +615,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void reconfigureAllConnectionsLocked() {
if (mAvailablePrimaryConnection != null) {
try {
@@ -776,6 +783,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
if (waiter.mAssignedConnection != null || waiter.mException != null) {
// Waiter is done waiting but has not woken up yet.
@@ -848,6 +856,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void wakeConnectionWaitersLocked() {
// Unpark all waiters that have requests that we can fulfill.
// This method is designed to not throw runtime exceptions, although we might send
@@ -910,6 +919,7 @@
}
// Might throw.
+ @GuardedBy("mLock")
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
// If the primary connection is available, acquire it now.
SQLiteConnection connection = mAvailablePrimaryConnection;
@@ -935,6 +945,7 @@
}
// Might throw.
+ @GuardedBy("mLock")
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
@@ -974,6 +985,7 @@
}
// Might throw.
+ @GuardedBy("mLock")
private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
try {
final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index c1c0812..ae1f57d 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2006,7 +2006,6 @@
* SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
* SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
* myDatabaseErrorHandler);
- * db.enableWriteAheadLogging();
* </pre></code>
* </p><p>
* Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging}
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
new file mode 100644
index 0000000..c811999
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -0,0 +1,184 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.CancellationSignal;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This is the common interface that all biometric authentication classes should implement.
+ * @hide
+ */
+public interface BiometricAuthenticator {
+
+ /**
+ * Container for biometric data
+ * @hide
+ */
+ abstract class BiometricIdentifier implements Parcelable {}
+
+ /**
+ * Container for callback data from {@link BiometricAuthenticator#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ class AuthenticationResult {
+ private BiometricIdentifier mIdentifier;
+ private CryptoObject mCryptoObject;
+ private int mUserId;
+
+ /**
+ * @hide
+ */
+ public AuthenticationResult() { }
+
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ mCryptoObject = crypto;
+ mIdentifier = identifier;
+ mUserId = userId;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return mCryptoObject;
+ }
+
+ /**
+ * Obtain the biometric identifier associated with this operation. Applications are strongly
+ * discouraged from associating specific identifiers with specific applications or
+ * operations.
+ * @hide
+ */
+ public BiometricIdentifier getId() {
+ return mIdentifier;
+ }
+
+ /**
+ * Obtain the userId for which this biometric was authenticated.
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+ };
+
+ /**
+ * Callback structure provided to {@link BiometricAuthenticator#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}. Users must provide
+ * an implementation of this for listening to biometric events.
+ */
+ abstract class AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ public void onAuthenticationAcquired(int acquireInfo) {}
+ };
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, at which point the crypto object becomes invalid. This
+ * operation can be canceled by using the provided cancel object. The application wil receive
+ * authentication errors through {@link AuthenticationCallback}. Calling
+ * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is occurring will stop
+ * the previous client and start a new authentication. The interrupted client will receive a
+ * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+
+ /**
+ * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+ * when {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called. This operation can be canceled by using the provided cancel
+ * object. The application wil receive authentication errors through
+ * {@link AuthenticationCallback}. Calling {@link BiometricAuthenticator#authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} while an existing
+ * authentication attempt is occurring will stop the previous client and start a new
+ * authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException If any of the arguments are null
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ */
+ void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback);
+}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
new file mode 100644
index 0000000..638f525
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -0,0 +1,168 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.hardware.fingerprint.FingerprintManager;
+
+/**
+ * Interface containing all of the fingerprint-specific constants.
+ * @hide
+ */
+public interface BiometricFingerprintConstants {
+ //
+ // Error messages from fingerprint hardware during initilization, enrollment, authentication or
+ // removal. Must agree with the list in fingerprint.h
+ //
+
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+ /**
+ * The operation was canceled because the fingerprint sensor is unavailable. For example,
+ * this may happen when the user is switched, the device is locked or another pending operation
+ * prevents or disables it.
+ */
+ public static final int FINGERPRINT_ERROR_CANCELED = 5;
+
+ /**
+ * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
+ * provided fingerprint id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
+ * expected to show the error message string if they happen, but are advised not to rely on the
+ * message id since they will be device and vendor-specific
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR = 8;
+
+ /**
+ * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
+ * Fingerprint authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return
+ * to fingerprint authentication, such as a "use fingerprint" button.
+ */
+ public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+
+ /**
+ * The user does not have any fingerprints enrolled.
+ */
+ public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+
+ /**
+ * The device does not have a fingerprint sensor.
+ */
+ public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+
+ //
+ // Image acquisition messages. Must agree with those in fingerprint.h
+ //
+
+ /**
+ * The image acquired was good.
+ */
+ public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+
+ /**
+ * Only a partial fingerprint image was detected. During enrollment, the user should be
+ * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
+ */
+ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+
+ /**
+ * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
+ * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
+ */
+ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+
+ /**
+ * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
+ * For example, it's reasonable return this after multiple
+ * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
+ * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
+ * when this is returned.
+ */
+ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+
+ /**
+ * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
+ * linear array sensors that require a swipe motion.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+
+ /**
+ * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
+ * linear array sensors, this could also happen if the finger was moved during acquisition.
+ * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
+ * longer.
+ */
+ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+ /**
+ * @hide
+ */
+ public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
new file mode 100644
index 0000000..496d9c5
--- /dev/null
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -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.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.security.keystore.AndroidKeyStoreProvider;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * @hide
+ */
+public class CryptoObject {
+ private final Object mCrypto;
+
+ public CryptoObject(@NonNull Signature signature) {
+ mCrypto = signature;
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ mCrypto = cipher;
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ mCrypto = mac;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ }
+
+ /**
+ * @hide
+ * @return the opId associated with this object or 0 if none
+ */
+ public final long getOpId() {
+ return mCrypto != null
+ ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ }
+};
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 40ee834..df64401 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -923,6 +923,14 @@
* {@link CameraCaptureSession#setRepeatingRequest} or
* {@link CameraCaptureSession#setRepeatingBurst}</p>
*
+ * <p>Individual physical camera settings will only be honored for camera session
+ * that was initialiazed with corresponding physical camera id output configuration
+ * {@link OutputConfiguration#setPhysicalCameraId} and the same output targets are
+ * also attached in the request by {@link CaptureRequest.Builder#addTarget}.</p>
+ *
+ * <p>The output is undefined for any logical camera streams in case valid physical camera
+ * settings are attached.</p>
+ *
* @param templateType An enumeration selecting the use case for this request. Not all template
* types are supported on every device. See the documentation for each template type for
* details.
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index bae2d04..0be45a0 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -17,7 +17,6 @@
package android.hardware.camera2;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
@@ -26,6 +25,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* <p>The total assembled results of a single image capture from the image sensor.</p>
@@ -49,9 +49,9 @@
*
* <p>For a logical multi-camera device, if the CaptureRequest contains a surface for an underlying
* physical camera, the corresponding {@link TotalCaptureResult} object will include the metadata
- * for that physical camera. And its keys and values can be accessed by
- * {@link #getPhysicalCameraKey}. If all requested surfaces are for the logical camera, no
- * metadata for physical camera will be included.</p>
+ * for that physical camera. And the mapping between the physical camera id and result metadata
+ * can be accessed via {@link #getPhysicalCameraResults}. If all requested surfaces are for the
+ * logical camera, no metadata for physical camera will be included.</p>
*
* <p>{@link TotalCaptureResult} objects are immutable.</p>
*
@@ -62,7 +62,7 @@
private final List<CaptureResult> mPartialResults;
private final int mSessionId;
// The map between physical camera id and capture result
- private final HashMap<String, CameraMetadataNative> mPhysicalCaptureResults;
+ private final HashMap<String, CaptureResult> mPhysicalCaptureResults;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -83,10 +83,12 @@
mSessionId = sessionId;
- mPhysicalCaptureResults = new HashMap<String, CameraMetadataNative>();
+ mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
+ CaptureResult physicalResult = new CaptureResult(
+ onePhysicalResult.getCameraMetadata(), parent, extras);
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
- onePhysicalResult.getCameraMetadata());
+ physicalResult);
}
}
@@ -101,7 +103,7 @@
mPartialResults = new ArrayList<>();
mSessionId = CameraCaptureSession.SESSION_ID_NONE;
- mPhysicalCaptureResults = new HashMap<String, CameraMetadataNative>();
+ mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
}
/**
@@ -132,36 +134,20 @@
}
/**
- * Get a capture result field value for a particular physical camera id.
+ * Get the map between physical camera ids and their capture result metadata
*
- * <p>The field definitions can be found in {@link CaptureResult}.</p>
- *
- * <p>This function can be called for logical camera devices, which are devices that have
+ * <p>This function can be called for logical multi-camera devices, which are devices that have
* REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to {@link
* CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of physical devices that
- * are backing the logical camera. The camera id included in physicalCameraId argument
- * selects an individual physical device, and returns its specific capture result field.</p>
+ * are backing the logical camera.</p>
*
- * <p>This function should only be called if one or more streams from the underlying
- * 'physicalCameraId' was requested by the corresponding capture request.</p>
- *
- * @throws IllegalArgumentException if the key was not valid, or the physicalCameraId is not
- * applicable to the current camera, or a stream from 'physicalCameraId' is not requested by the
- * corresponding capture request.
- *
- * @param key The result field to read.
- * @param physicalCameraId The physical camera the result originates from.
- *
- * @return The value of that key, or {@code null} if the field is not set.
- */
- @Nullable
- public <T> T getPhysicalCameraKey(Key<T> key, @NonNull String physicalCameraId) {
- if (!mPhysicalCaptureResults.containsKey(physicalCameraId)) {
- throw new IllegalArgumentException(
- "No TotalCaptureResult exists for physical camera " + physicalCameraId);
- }
+ * <p>If one or more streams from the underlying physical cameras were requested by the
+ * corresponding capture request, this function returns the result metadata for those physical
+ * cameras. Otherwise, an empty map is returned.</p>
- CameraMetadataNative physicalMetadata = mPhysicalCaptureResults.get(physicalCameraId);
- return physicalMetadata.get(key);
+ * @return unmodifiable map between physical camera ids and their capture result metadata
+ */
+ public Map<String, CaptureResult> getPhysicalCameraResults() {
+ return Collections.unmodifiableMap(mPhysicalCaptureResults);
}
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index eb4bced..b205e2c 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -368,7 +368,7 @@
* desirable for the camera application to request streams from individual physical cameras.
* This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
*
- * <p>The valid physical camera id can be queried by {@link
+ * <p>The valid physical camera ids can be queried by {@link
* android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
* </p>
*
diff --git a/core/java/com/android/internal/car/ICarServiceHelper.aidl b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
similarity index 70%
rename from core/java/com/android/internal/car/ICarServiceHelper.aidl
rename to core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
index 9ee330b..9070777 100644
--- a/core/java/com/android/internal/car/ICarServiceHelper.aidl
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl
@@ -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,11 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.car;
+package android.hardware.display;
-/**
- * Helper API for car service. Only for itneraction between system server and car service.
- * @hide
- */
-interface ICarServiceHelper {
-}
+parcelable AmbientBrightnessDayStats;
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
new file mode 100644
index 0000000..00f3c36
--- /dev/null
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -0,0 +1,211 @@
+/*
+ * 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.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+/**
+ * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
+ * {@see DisplayManager.getAmbientBrightnessStats()}
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
+
+ /** The localdate for which brightness stats are being tracked */
+ private final LocalDate mLocalDate;
+
+ /** Ambient brightness values for creating bucket boundaries from */
+ private final float[] mBucketBoundaries;
+
+ /** Stats of how much time (in seconds) was spent in each of the buckets */
+ private final float[] mStats;
+
+ /**
+ * @hide
+ */
+ public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+ @NonNull float[] bucketBoundaries) {
+ this(localDate, bucketBoundaries, null);
+ }
+
+ /**
+ * @hide
+ */
+ public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+ @NonNull float[] bucketBoundaries, float[] stats) {
+ Preconditions.checkNotNull(localDate);
+ Preconditions.checkNotNull(bucketBoundaries);
+ Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+ "bucketBoundaries");
+ if (bucketBoundaries.length < 1) {
+ throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
+ }
+ checkSorted(bucketBoundaries);
+ if (stats == null) {
+ stats = new float[bucketBoundaries.length];
+ } else {
+ Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+ if (bucketBoundaries.length != stats.length) {
+ throw new IllegalArgumentException(
+ "Bucket boundaries and stats must be of same size.");
+ }
+ }
+ mLocalDate = localDate;
+ mBucketBoundaries = bucketBoundaries;
+ mStats = stats;
+ }
+
+ public LocalDate getLocalDate() {
+ return mLocalDate;
+ }
+
+ public float[] getStats() {
+ return mStats;
+ }
+
+ public float[] getBucketBoundaries() {
+ return mBucketBoundaries;
+ }
+
+ private AmbientBrightnessDayStats(Parcel source) {
+ mLocalDate = LocalDate.parse(source.readString());
+ mBucketBoundaries = source.createFloatArray();
+ mStats = source.createFloatArray();
+ }
+
+ public static final Creator<AmbientBrightnessDayStats> CREATOR =
+ new Creator<AmbientBrightnessDayStats>() {
+
+ @Override
+ public AmbientBrightnessDayStats createFromParcel(Parcel source) {
+ return new AmbientBrightnessDayStats(source);
+ }
+
+ @Override
+ public AmbientBrightnessDayStats[] newArray(int size) {
+ return new AmbientBrightnessDayStats[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
+ return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
+ other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = result * prime + mLocalDate.hashCode();
+ result = result * prime + Arrays.hashCode(mBucketBoundaries);
+ result = result * prime + Arrays.hashCode(mStats);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bucketBoundariesString = new StringBuilder();
+ StringBuilder statsString = new StringBuilder();
+ for (int i = 0; i < mBucketBoundaries.length; i++) {
+ if (i != 0) {
+ bucketBoundariesString.append(", ");
+ statsString.append(", ");
+ }
+ bucketBoundariesString.append(mBucketBoundaries[i]);
+ statsString.append(mStats[i]);
+ }
+ return new StringBuilder()
+ .append(mLocalDate).append(" ")
+ .append("{").append(bucketBoundariesString).append("} ")
+ .append("{").append(statsString).append("}").toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mLocalDate.toString());
+ dest.writeFloatArray(mBucketBoundaries);
+ dest.writeFloatArray(mStats);
+ }
+
+ /** @hide */
+ public void log(float ambientBrightness, float durationSec) {
+ int bucketIndex = getBucketIndex(ambientBrightness);
+ if (bucketIndex >= 0) {
+ mStats[bucketIndex] += durationSec;
+ }
+ }
+
+ private int getBucketIndex(float ambientBrightness) {
+ if (ambientBrightness < mBucketBoundaries[0]) {
+ return -1;
+ }
+ int low = 0;
+ int high = mBucketBoundaries.length - 1;
+ while (low < high) {
+ int mid = (low + high) / 2;
+ if (mBucketBoundaries[mid] <= ambientBrightness
+ && ambientBrightness < mBucketBoundaries[mid + 1]) {
+ return mid;
+ } else if (mBucketBoundaries[mid] < ambientBrightness) {
+ low = mid + 1;
+ } else if (mBucketBoundaries[mid] > ambientBrightness) {
+ high = mid - 1;
+ }
+ }
+ return low;
+ }
+
+ private static void checkSorted(float[] values) {
+ if (values.length <= 1) {
+ return;
+ }
+ float prevValue = values[0];
+ for (int i = 1; i < values.length; i++) {
+ Preconditions.checkState(prevValue < values[i]);
+ prevValue = values[i];
+ }
+ return;
+ }
+}
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 2301824..02eb28c 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -54,19 +54,30 @@
/** Most recent battery level when brightness was changed or Float.NaN */
public final float batteryLevel;
+ /** Factor applied to brightness due to battery level, 0.0-1.0 */
+ public final float powerBrightnessFactor;
+
/** Color filter active to provide night mode */
public final boolean nightMode;
/** If night mode color filter is active this will be the temperature in kelvin */
public final int colorTemperature;
- /** Brightness le vel before slider adjustment */
+ /** Brightness level before slider adjustment */
public final float lastBrightness;
+ /** Whether brightness configuration is default version */
+ public final boolean isDefaultBrightnessConfig;
+
+ /** Whether brightness curve includes a user brightness point */
+ public final boolean isUserSetBrightness;
+
+
/** @hide */
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
- boolean nightMode, int colorTemperature, float lastBrightness) {
+ float powerBrightnessFactor, boolean nightMode, int colorTemperature,
+ float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -74,9 +85,12 @@
this.luxValues = luxValues;
this.luxTimestamps = luxTimestamps;
this.batteryLevel = batteryLevel;
+ this.powerBrightnessFactor = powerBrightnessFactor;
this.nightMode = nightMode;
this.colorTemperature = colorTemperature;
this.lastBrightness = lastBrightness;
+ this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ this.isUserSetBrightness = isUserSetBrightness;
}
/** @hide */
@@ -88,9 +102,12 @@
this.luxValues = other.luxValues;
this.luxTimestamps = other.luxTimestamps;
this.batteryLevel = other.batteryLevel;
+ this.powerBrightnessFactor = other.powerBrightnessFactor;
this.nightMode = other.nightMode;
this.colorTemperature = other.colorTemperature;
this.lastBrightness = other.lastBrightness;
+ this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
+ this.isUserSetBrightness = other.isUserSetBrightness;
}
private BrightnessChangeEvent(Parcel source) {
@@ -101,9 +118,12 @@
luxValues = source.createFloatArray();
luxTimestamps = source.createLongArray();
batteryLevel = source.readFloat();
+ powerBrightnessFactor = source.readFloat();
nightMode = source.readBoolean();
colorTemperature = source.readInt();
lastBrightness = source.readFloat();
+ isDefaultBrightnessConfig = source.readBoolean();
+ isUserSetBrightness = source.readBoolean();
}
public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -130,9 +150,12 @@
dest.writeFloatArray(luxValues);
dest.writeLongArray(luxTimestamps);
dest.writeFloat(batteryLevel);
+ dest.writeFloat(powerBrightnessFactor);
dest.writeBoolean(nightMode);
dest.writeInt(colorTemperature);
dest.writeFloat(lastBrightness);
+ dest.writeBoolean(isDefaultBrightnessConfig);
+ dest.writeBoolean(isUserSetBrightness);
}
/** @hide */
@@ -144,9 +167,12 @@
private float[] mLuxValues;
private long[] mLuxTimestamps;
private float mBatteryLevel;
+ private float mPowerBrightnessFactor;
private boolean mNightMode;
private int mColorTemperature;
private float mLastBrightness;
+ private boolean mIsDefaultBrightnessConfig;
+ private boolean mIsUserSetBrightness;
/** {@see BrightnessChangeEvent#brightness} */
public Builder setBrightness(float brightness) {
@@ -190,6 +216,12 @@
return this;
}
+ /** {@see BrightnessChangeEvent#powerSaveBrightness} */
+ public Builder setPowerBrightnessFactor(float powerBrightnessFactor) {
+ mPowerBrightnessFactor = powerBrightnessFactor;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#nightMode} */
public Builder setNightMode(boolean nightMode) {
mNightMode = nightMode;
@@ -208,11 +240,24 @@
return this;
}
+ /** {@see BrightnessChangeEvent#isDefaultBrightnessConfig} */
+ public Builder setIsDefaultBrightnessConfig(boolean isDefaultBrightnessConfig) {
+ mIsDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#userBrightnessPoint} */
+ public Builder setUserBrightnessPoint(boolean isUserSetBrightness) {
+ mIsUserSetBrightness = isUserSetBrightness;
+ return this;
+ }
+
/** Builds a BrightnessChangeEvent */
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mNightMode, mColorTemperature, mLastBrightness);
+ mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+ mIsDefaultBrightnessConfig, mIsUserSetBrightness);
}
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4de4880..6b2059e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -631,6 +631,16 @@
}
/**
+ * Fetch {@link AmbientBrightnessDayStats}s.
+ *
+ * @hide until we make it a system api
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ return mGlobal.getAmbientBrightnessStats();
+ }
+
+ /**
* Sets the global display brightness configuration.
*
* @hide
@@ -639,7 +649,7 @@
@TestApi
@RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
public void setBrightnessConfiguration(BrightnessConfiguration c) {
- setBrightnessConfigurationForUser(c, UserHandle.myUserId(), mContext.getPackageName());
+ setBrightnessConfigurationForUser(c, mContext.getUserId(), mContext.getPackageName());
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 2d5f5e0..d7f7c86 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -525,6 +525,21 @@
}
}
+ /**
+ * Retrieves ambient brightness stats.
+ */
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ try {
+ ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
+ if (stats == null) {
+ return Collections.emptyList();
+ }
+ return stats.getList();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 1cfad4f..f468942 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -174,9 +174,9 @@
public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
/**
- * Persist brightness slider events.
+ * Persist brightness slider events and ambient brightness stats.
*/
- public abstract void persistBrightnessSliderEvents();
+ public abstract void persistBrightnessTrackerState();
/**
* Notifies the display manager that resource overlays have changed.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 13599cf..0571ae1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -87,6 +87,9 @@
// Requires BRIGHTNESS_SLIDER_USAGE permission.
ParceledListSlice getBrightnessEvents(String callingPackage);
+ // Requires ACCESS_AMBIENT_LIGHT_STATS permission.
+ ParceledListSlice getAmbientBrightnessStats();
+
// Sets the global brightness configuration for a given user. Requires
// CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not
// the same as the calling user.
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index c307634..c7ce8fa 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -15,6 +15,7 @@
*/
package android.hardware.fingerprint;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -22,7 +23,7 @@
* Container for fingerprint metadata.
* @hide
*/
-public final class Fingerprint implements Parcelable {
+public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
private CharSequence mName;
private int mGroupId;
private int mFingerId;
diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/fingerprint/FingerprintDialog.java
index 6b7fab7..49835963 100644
--- a/core/java/android/hardware/fingerprint/FingerprintDialog.java
+++ b/core/java/android/hardware/fingerprint/FingerprintDialog.java
@@ -23,19 +23,25 @@
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.DialogInterface;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.CryptoObject;
import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.text.TextUtils;
+import java.security.Signature;
import java.util.concurrent.Executor;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
/**
* A class that manages a system-provided fingerprint dialog.
*/
-public class FingerprintDialog {
+public class FingerprintDialog implements BiometricAuthenticator, BiometricFingerprintConstants {
/**
* @hide
@@ -200,6 +206,7 @@
}
}
+ private PackageManager mPackageManager;
private FingerprintManager mFingerprintManager;
private Bundle mBundle;
private ButtonInfo mPositiveButtonInfo;
@@ -227,37 +234,209 @@
mPositiveButtonInfo = positiveButtonInfo;
mNegativeButtonInfo = negativeButtonInfo;
mFingerprintManager = context.getSystemService(FingerprintManager.class);
+ mPackageManager = context.getPackageManager();
}
/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ */
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+ public CryptoObject(@NonNull Signature signature) {
+ super(signature);
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ super(cipher);
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ super(mac);
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return super.getSignature();
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return super.getCipher();
+ }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return super.getMac();
+ }
+ }
+
+ /**
+ * Container for callback data from {@link #authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} and
+ * {@link #authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ */
+ public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
+ /**
+ * Authentication result
+ * @param crypto
+ * @param identifier
+ * @param userId
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ int userId) {
+ super(crypto, identifier, userId);
+ }
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link #authenticate(
+ * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ */
+ public CryptoObject getCryptoObject() {
+ return (CryptoObject) super.getCryptoObject();
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FingerprintDialog#authenticate(CancellationSignal,
+ * Executor, AuthenticationCallback)} or {@link FingerprintDialog#authenticate(CryptoObject,
+ * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
+ * of this for listening to authentication events.
+ */
+ public static abstract class AuthenticationCallback extends
+ BiometricAuthenticator.AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further actions will be made on this object.
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+ * please clean it."
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown on an UI
+ */
+ @Override
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+ /**
+ * Called when a biometric is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+ /**
+ * Called when a biometric is valid but not recognized.
+ */
+ @Override
+ public void onAuthenticationFailed() {}
+
+ /**
+ * Called when a biometric has been acquired, but hasn't been processed yet.
+ * @hide
+ */
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @param result An object containing authentication-related data
+ * @hide
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ result.getId(),
+ result.getUserId()));
+ }
+ }
+
+
+ /**
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
+ }
+
+ /**
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @hide
+ */
+ @Override
+ public void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+ throw new IllegalArgumentException("Callback cannot be casted");
+ }
+ authenticate(cancel, executor, (AuthenticationCallback) callback);
+ }
+
+
+ /**
* This call warms up the fingerprint hardware, displays a system-provided dialog,
* and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog, at which point the crypto object becomes invalid.
- * This operation can be canceled by using the provided cancel object. The application will
- * receive authentication errors through {@link AuthenticationCallback}, and button events
- * through the corresponding callback set in
- * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
- * It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)}, or when the user dismisses the system-provided dialog, at which point
+ * the crypto object becomes invalid. This operation can be canceled by using the provided
+ * cancel object. The application will receive authentication errors through
+ * {@link AuthenticationCallback}, and button events through the
+ * corresponding callback set in {@link Builder#setNegativeButton(CharSequence,
+ * Executor, DialogInterface.OnClickListener)}. It is safe to reuse the
+ * {@link FingerprintDialog} object, and calling {@link FingerprintDialog#authenticate(
+ * CancellationSignal, Executor, AuthenticationCallback)} while an
+ * existing authentication attempt is occurring will stop the previous client and start a
+ * new authentication. The interrupted client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
*
- * @throws IllegalArgumentException if any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null
*
- * @param crypto object associated with the call
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
+ * @param crypto Object associated with the call
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
*/
@RequiresPermission(USE_FINGERPRINT)
- public void authenticate(@NonNull FingerprintManager.CryptoObject crypto,
+ public void authenticate(@NonNull CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
callback);
}
@@ -265,29 +444,57 @@
/**
* This call warms up the fingerprint hardware, displays a system-provided dialog,
* and starts scanning for a fingerprint. It terminates when
- * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
- * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
- * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
- * dismisses the system-provided dialog. This operation can be canceled by using the provided
- * cancel object. The application will receive authentication errors through
- * {@link AuthenticationCallback}, and button events through the corresponding callback set in
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(
+ * AuthenticationResult)} is called, or when the user dismisses the system-provided dialog.
+ * This operation can be canceled by using the provided cancel object. The application will
+ * receive authentication errors through {@link AuthenticationCallback},
+ * and button events through the corresponding callback set in
* {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
* It is safe to reuse the {@link FingerprintDialog} object, and calling
- * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
- * while an existing authentication attempt is occurring will stop the previous client and
- * start a new authentication. The interrupted client will receive a cancelled notification
- * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ * {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * AuthenticationCallback)} while an existing authentication attempt is
+ * occurring will stop the previous client and start a new authentication. The interrupted
+ * client will receive a cancelled notification through
+ * {@link AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}.
*
- * @throws IllegalArgumentException if any of the arguments are null
+ * @throws IllegalArgumentException If any of the arguments are null
*
- * @param cancel an object that can be used to cancel authentication
- * @param executor an executor to handle callback events
- * @param callback an object to receive authentication events
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
*/
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull FingerprintManager.AuthenticationCallback callback) {
+ @NonNull AuthenticationCallback callback) {
+ if (handlePreAuthenticationErrors(callback, executor)) {
+ return;
+ }
mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
}
+
+ private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
+ Executor executor) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ sendError(FINGERPRINT_ERROR_HW_NOT_PRESENT, callback, executor);
+ return true;
+ } else if (!mFingerprintManager.isHardwareDetected()) {
+ sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, callback, executor);
+ return true;
+ } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ sendError(FINGERPRINT_ERROR_NO_FINGERPRINTS, callback, executor);
+ return true;
+ }
+ return false;
+ }
+
+ private void sendError(int error, AuthenticationCallback callback, Executor executor) {
+ executor.execute(() -> {
+ callback.onAuthenticationError(error, mFingerprintManager.getErrorString(
+ error, 0 /* vendorCode */));
+ });
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 62d92c4..92d6bbb 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -27,6 +27,8 @@
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -38,7 +40,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
@@ -51,9 +52,14 @@
/**
* A class that coordinates access to the fingerprint hardware.
+ * @deprecated See {@link FingerprintDialog} which shows a system-provided dialog upon starting
+ * authentication. In a world where devices may have in-display fingerprint sensors, it's much
+ * more realistic to have a system-provided authentication dialog since the in-display sensor
+ * location may vary by vendor/device.
*/
+@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
-public class FingerprintManager {
+public class FingerprintManager implements BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
@@ -64,147 +70,14 @@
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
- //
- // Error messages from fingerprint hardware during initilization, enrollment, authentication or
- // removal. Must agree with the list in fingerprint.h
- //
-
- /**
- * The hardware is unavailable. Try again later.
- */
- public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-
- /**
- * Error state returned when the sensor was unable to process the current image.
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
-
- /**
- * Error state returned when the current request has been running too long. This is intended to
- * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
- * platform and sensor-specific, but is generally on the order of 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
-
- /**
- * Error state returned for operations like enrollment; the operation cannot be completed
- * because there's not enough storage remaining to complete the operation.
- */
- public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
-
- /**
- * The operation was canceled because the fingerprint sensor is unavailable. For example,
- * this may happen when the user is switched, the device is locked or another pending operation
- * prevents or disables it.
- */
- public static final int FINGERPRINT_ERROR_CANCELED = 5;
-
- /**
- * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
- * provided fingerprint id was incorrect.
- *
- * @hide
- */
- public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
-
- /**
- * The operation was canceled because the API is locked out due to too many attempts.
- * This occurs after 5 failed attempts, and lasts for 30 seconds.
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * These messages are typically reserved for internal operations such as enrollment, but may be
- * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
- * expected to show the error message string if they happen, but are advised not to rely on the
- * message id since they will be device and vendor-specific
- */
- public static final int FINGERPRINT_ERROR_VENDOR = 8;
-
- /**
- * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
- * Fingerprint authentication is disabled until the user unlocks with strong authentication
- * (PIN/Pattern/Password)
- */
- public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
-
- /**
- * The user canceled the operation. Upon receiving this, applications should use alternate
- * authentication (e.g. a password). The application should also provide the means to return
- * to fingerprint authentication, such as a "use fingerprint" button.
- */
- public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
-
- /**
- * @hide
- */
- public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
-
- //
- // Image acquisition messages. Must agree with those in fingerprint.h
- //
-
- /**
- * The image acquired was good.
- */
- public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
-
- /**
- * Only a partial fingerprint image was detected. During enrollment, the user should be
- * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
- */
- public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
-
- /**
- * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
- * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
- */
- public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
-
- /**
- * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
- * For example, it's reasonable return this after multiple
- * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
- * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
- * when this is returned.
- */
- public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
-
- /**
- * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
- * linear array sensors that require a swipe motion.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
-
- /**
- * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
- * linear array sensors, this could also happen if the finger was moved during acquisition.
- * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
- * longer.
- */
- public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
-
- /**
- * Hardware vendors may extend this list if there are conditions that do not fall under one of
- * the above categories. Vendors are responsible for providing error strings for these errors.
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
- /**
- * @hide
- */
- public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
-
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
- private AuthenticationCallback mAuthenticationCallback;
+ private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
- private CryptoObject mCryptoObject;
+ private android.hardware.biometrics.CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
private Executor mExecutor;
@@ -217,9 +90,9 @@
}
private class OnAuthenticationCancelListener implements OnCancelListener {
- private CryptoObject mCrypto;
+ private android.hardware.biometrics.CryptoObject mCrypto;
- public OnAuthenticationCancelListener(CryptoObject crypto) {
+ public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
mCrypto = crypto;
}
@@ -233,18 +106,17 @@
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
- public static final class CryptoObject {
-
+ public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
public CryptoObject(@NonNull Signature signature) {
- mCrypto = signature;
+ super(signature);
}
public CryptoObject(@NonNull Cipher cipher) {
- mCrypto = cipher;
+ super(cipher);
}
public CryptoObject(@NonNull Mac mac) {
- mCrypto = mac;
+ super(mac);
}
/**
@@ -252,7 +124,7 @@
* @return {@link Signature} object or null if this doesn't contain one.
*/
public Signature getSignature() {
- return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ return super.getSignature();
}
/**
@@ -260,7 +132,7 @@
* @return {@link Cipher} object or null if this doesn't contain one.
*/
public Cipher getCipher() {
- return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ return super.getCipher();
}
/**
@@ -268,20 +140,9 @@
* @return {@link Mac} object or null if this doesn't contain one.
*/
public Mac getMac() {
- return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ return super.getMac();
}
-
- /**
- * @hide
- * @return the opId associated with this object or 0 if none
- */
- public long getOpId() {
- return mCrypto != null ?
- AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
- }
-
- private final Object mCrypto;
- };
+ }
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
@@ -334,13 +195,15 @@
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
*/
- public static abstract class AuthenticationCallback {
+ public static abstract class AuthenticationCallback
+ extends BiometricAuthenticator.AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
+ @Override
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
@@ -350,6 +213,7 @@
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
+ @Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
@@ -361,6 +225,7 @@
/**
* Called when a fingerprint is valid but not recognized.
*/
+ @Override
public void onAuthenticationFailed() { }
/**
@@ -369,7 +234,19 @@
* @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
* @hide
*/
+ @Override
public void onAuthenticationAcquired(int acquireInfo) {}
+
+ /**
+ * @hide
+ * @param result
+ */
+ @Override
+ public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+ onAuthenticationSucceeded(new AuthenticationResult(
+ (CryptoObject) result.getCryptoObject(),
+ (Fingerprint) result.getId(), result.getUserId()));
+ }
};
/**
@@ -489,11 +366,16 @@
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
+ * @deprecated See {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * FingerprintDialog.AuthenticationCallback)} and {@link FingerprintDialog#authenticate(
+ * FingerprintDialog.CryptoObject, CancellationSignal, Executor,
+ * FingerprintDialog.AuthenticationCallback)}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
+ authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
}
/**
@@ -554,12 +436,12 @@
* @param userId the user ID that the fingerprint hardware will authenticate for.
*/
private void authenticate(int userId,
- @Nullable CryptoObject crypto,
+ @Nullable android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
mCryptoObject = crypto;
if (cancel.isCanceled()) {
Log.w(TAG, "authentication already canceled");
@@ -598,7 +480,7 @@
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -614,7 +496,7 @@
if (callback == null) {
throw new IllegalArgumentException("Must supply a calback");
}
- authenticate(UserHandle.myUserId(), null, cancel, bundle, executor, receiver, callback);
+ authenticate(mContext.getUserId(), null, cancel, bundle, executor, receiver, callback);
}
/**
@@ -626,12 +508,12 @@
* @param callback
* @hide
*/
- public void authenticate(@NonNull CryptoObject crypto,
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull Bundle bundle,
@NonNull @CallbackExecutor Executor executor,
@NonNull IFingerprintDialogReceiver receiver,
- @NonNull AuthenticationCallback callback) {
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
if (crypto == null) {
throw new IllegalArgumentException("Must supply a crypto object");
}
@@ -648,9 +530,10 @@
throw new IllegalArgumentException("Must supply a receiver");
}
if (callback == null) {
- throw new IllegalArgumentException("Must supply a calback");
+ throw new IllegalArgumentException("Must supply a callback");
}
- authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback);
+ authenticate(mContext.getUserId(), crypto, cancel,
+ bundle, executor, receiver, callback);
}
/**
@@ -841,19 +724,22 @@
*/
@RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints() {
- return getEnrolledFingerprints(UserHandle.myUserId());
+ return getEnrolledFingerprints(mContext.getUserId());
}
/**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
+ * @deprecated See {@link FingerprintDialog} and
+ * {@link FingerprintDialog#FINGERPRINT_ERROR_NO_FINGERPRINTS}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
return mService.hasEnrolledFingerprints(
- UserHandle.myUserId(), mContext.getOpPackageName());
+ mContext.getUserId(), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -879,7 +765,10 @@
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
+ * @deprecated See {@link FingerprintDialog} and
+ * {@link FingerprintDialog#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
+ @Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
@@ -1049,8 +938,8 @@
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
+ final BiometricAuthenticator.AuthenticationResult result =
+ new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1126,7 +1015,7 @@
}
}
- private void cancelAuthentication(CryptoObject cryptoObject) {
+ private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
if (mService != null) try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1160,6 +1049,12 @@
case FINGERPRINT_ERROR_USER_CANCELED:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_user_canceled);
+ case FINGERPRINT_ERROR_NO_FINGERPRINTS:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_no_fingerprints);
+ case FINGERPRINT_ERROR_HW_NOT_PRESENT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_hw_not_present);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index b00f603..e1d7edf 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -213,6 +213,7 @@
private final boolean mIsBgScanSupported;
private final Set<Integer> mSupportedProgramTypes;
private final Set<Integer> mSupportedIdentifierTypes;
+ @Nullable private final Map<String, Integer> mDabFrequencyTable;
@NonNull private final Map<String, String> mVendorInfo;
/** @hide */
@@ -221,6 +222,7 @@
boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
@ProgramSelector.ProgramType int[] supportedProgramTypes,
@ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
+ @Nullable Map<String, Integer> dabFrequencyTable,
Map<String, String> vendorInfo) {
mId = id;
mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
@@ -236,6 +238,13 @@
mIsBgScanSupported = isBgScanSupported;
mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
+ if (dabFrequencyTable != null) {
+ for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) {
+ Objects.requireNonNull(entry.getKey());
+ Objects.requireNonNull(entry.getValue());
+ }
+ }
+ mDabFrequencyTable = dabFrequencyTable;
mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
}
@@ -363,6 +372,19 @@
}
/**
+ * A frequency table for Digital Audio Broadcasting (DAB).
+ *
+ * The key is a channel name, i.e. 5A, 7B.
+ *
+ * The value is a frequency, in kHz.
+ *
+ * @return a frequency table, or {@code null} if the module doesn't support DAB
+ */
+ public @Nullable Map<String, Integer> getDabFrequencyTable() {
+ return mDabFrequencyTable;
+ }
+
+ /**
* A map of vendor-specific opaque strings, passed from HAL without changes.
* Format of these strings can vary across vendors.
*
@@ -403,6 +425,7 @@
mIsBgScanSupported = in.readInt() == 1;
mSupportedProgramTypes = arrayToSet(in.createIntArray());
mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
+ mDabFrequencyTable = Utils.readStringIntMap(in);
mVendorInfo = Utils.readStringMap(in);
}
@@ -433,6 +456,7 @@
dest.writeInt(mIsBgScanSupported ? 1 : 0);
dest.writeIntArray(setToArray(mSupportedProgramTypes));
dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
+ Utils.writeStringIntMap(dest, mDabFrequencyTable);
Utils.writeStringMap(dest, mVendorInfo);
}
@@ -456,67 +480,31 @@
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + mId;
- result = prime * result + mServiceName.hashCode();
- result = prime * result + mClassId;
- result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode());
- result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode());
- result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());
- result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode());
- result = prime * result + mNumTuners;
- result = prime * result + mNumAudioSources;
- result = prime * result + (mIsCaptureSupported ? 1 : 0);
- result = prime * result + Arrays.hashCode(mBands);
- result = prime * result + (mIsBgScanSupported ? 1 : 0);
- result = prime * result + mVendorInfo.hashCode();
- return result;
+ return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
+ mSerial, mNumTuners, mNumAudioSources, mIsCaptureSupported, mBands,
+ mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
}
@Override
public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (!(obj instanceof ModuleProperties))
- return false;
+ if (this == obj) return true;
+ if (!(obj instanceof ModuleProperties)) return false;
ModuleProperties other = (ModuleProperties) obj;
- if (mId != other.getId())
- return false;
+
+ if (mId != other.getId()) return false;
if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
- if (mClassId != other.getClassId())
- return false;
- if (mImplementor == null) {
- if (other.getImplementor() != null)
- return false;
- } else if (!mImplementor.equals(other.getImplementor()))
- return false;
- if (mProduct == null) {
- if (other.getProduct() != null)
- return false;
- } else if (!mProduct.equals(other.getProduct()))
- return false;
- if (mVersion == null) {
- if (other.getVersion() != null)
- return false;
- } else if (!mVersion.equals(other.getVersion()))
- return false;
- if (mSerial == null) {
- if (other.getSerial() != null)
- return false;
- } else if (!mSerial.equals(other.getSerial()))
- return false;
- if (mNumTuners != other.getNumTuners())
- return false;
- if (mNumAudioSources != other.getNumAudioSources())
- return false;
- if (mIsCaptureSupported != other.isCaptureSupported())
- return false;
- if (!Arrays.equals(mBands, other.getBands()))
- return false;
- if (mIsBgScanSupported != other.isBackgroundScanningSupported())
- return false;
- if (!mVendorInfo.equals(other.mVendorInfo)) return false;
+ if (mClassId != other.mClassId) return false;
+ if (!Objects.equals(mImplementor, other.mImplementor)) return false;
+ if (!Objects.equals(mProduct, other.mProduct)) return false;
+ if (!Objects.equals(mVersion, other.mVersion)) return false;
+ if (!Objects.equals(mSerial, other.mSerial)) return false;
+ if (mNumTuners != other.mNumTuners) return false;
+ if (mNumAudioSources != other.mNumAudioSources) return false;
+ if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
+ if (!Objects.equals(mBands, other.mBands)) return false;
+ if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
+ if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
+ if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
return true;
}
}
diff --git a/core/java/android/hardware/radio/Utils.java b/core/java/android/hardware/radio/Utils.java
index f1b5897..9887f78 100644
--- a/core/java/android/hardware/radio/Utils.java
+++ b/core/java/android/hardware/radio/Utils.java
@@ -56,6 +56,29 @@
return map;
}
+ static void writeStringIntMap(@NonNull Parcel dest, @Nullable Map<String, Integer> map) {
+ if (map == null) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(map.size());
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeInt(entry.getValue());
+ }
+ }
+
+ static @NonNull Map<String, Integer> readStringIntMap(@NonNull Parcel in) {
+ int size = in.readInt();
+ Map<String, Integer> map = new HashMap<>();
+ while (size-- > 0) {
+ String key = in.readString();
+ int value = in.readInt();
+ map.put(key, value);
+ }
+ return map;
+ }
+
static <T extends Parcelable> void writeSet(@NonNull Parcel dest, @Nullable Set<T> set) {
if (set == null) {
dest.writeInt(0);
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
index d9b7c5b..00070fe 100644
--- a/core/java/android/hardware/usb/AccessoryFilter.java
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -16,6 +16,11 @@
package android.hardware.usb;
+import android.annotation.NonNull;
+import android.service.usb.UsbAccessoryFilterProto;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -142,4 +147,17 @@
"\", mModel=\"" + mModel +
"\", mVersion=\"" + mVersion + "\"]";
}
+
+ /**
+ * Write a description of the filter to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("manufacturer", UsbAccessoryFilterProto.MANUFACTURER, mManufacturer);
+ dump.write("model", UsbAccessoryFilterProto.MODEL, mModel);
+ dump.write("version", UsbAccessoryFilterProto.VERSION, mVersion);
+
+ dump.end(token);
+ }
}
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
index 439c629..6f1aff7 100644
--- a/core/java/android/hardware/usb/DeviceFilter.java
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -16,8 +16,12 @@
package android.hardware.usb;
+import android.annotation.NonNull;
+import android.service.usb.UsbDeviceFilterProto;
import android.util.Slog;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -310,4 +314,22 @@
",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
"]";
}
+
+ /**
+ * Write a description of the filter to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("vendor_id", UsbDeviceFilterProto.VENDOR_ID, mVendorId);
+ dump.write("product_id", UsbDeviceFilterProto.PRODUCT_ID, mProductId);
+ dump.write("class", UsbDeviceFilterProto.CLASS, mClass);
+ dump.write("subclass", UsbDeviceFilterProto.SUBCLASS, mSubclass);
+ dump.write("protocol", UsbDeviceFilterProto.PROTOCOL, mProtocol);
+ dump.write("manufacturer_name", UsbDeviceFilterProto.MANUFACTURER_NAME, mManufacturerName);
+ dump.write("product_name", UsbDeviceFilterProto.PRODUCT_NAME, mProductName);
+ dump.write("serial_number", UsbDeviceFilterProto.SERIAL_NUMBER, mSerialNumber);
+
+ dump.end(token);
+ }
}
diff --git a/core/java/android/hardware/usb/UsbConfiguration.java b/core/java/android/hardware/usb/UsbConfiguration.java
index a171570..6ce4201 100644
--- a/core/java/android/hardware/usb/UsbConfiguration.java
+++ b/core/java/android/hardware/usb/UsbConfiguration.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+
import com.android.internal.util.Preconditions;
/**
@@ -106,6 +107,17 @@
}
/**
+ * Returns the attributes of this configuration
+ *
+ * @return the configuration's attributes
+ *
+ * @hide
+ */
+ public int getAttributes() {
+ return mAttributes;
+ }
+
+ /**
* Returns the configuration's max power consumption, in milliamps.
*
* @return the configuration's max power
diff --git a/core/java/android/hardware/usb/UsbConstants.java b/core/java/android/hardware/usb/UsbConstants.java
index 0e8d47c..215e9d5 100644
--- a/core/java/android/hardware/usb/UsbConstants.java
+++ b/core/java/android/hardware/usb/UsbConstants.java
@@ -16,6 +16,8 @@
package android.hardware.usb;
+import android.service.ServiceProtoEnums;
+
/**
* Contains constants for the USB protocol.
* These constants correspond to definitions in linux/usb/ch9.h in the linux kernel.
@@ -35,12 +37,12 @@
* Used to signify direction of data for a {@link UsbEndpoint} is OUT (host to device)
* @see UsbEndpoint#getDirection
*/
- public static final int USB_DIR_OUT = 0;
+ public static final int USB_DIR_OUT = ServiceProtoEnums.USB_ENDPOINT_DIR_OUT; // 0
/**
* Used to signify direction of data for a {@link UsbEndpoint} is IN (device to host)
* @see UsbEndpoint#getDirection
*/
- public static final int USB_DIR_IN = 0x80;
+ public static final int USB_DIR_IN = ServiceProtoEnums.USB_ENDPOINT_DIR_IN; // 0x80
/**
* Bitmask used for extracting the {@link UsbEndpoint} number its address field.
@@ -63,22 +65,26 @@
* Control endpoint type (endpoint zero)
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_CONTROL = 0;
+ public static final int USB_ENDPOINT_XFER_CONTROL =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_CONTROL; // 0
/**
* Isochronous endpoint type (currently not supported)
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_ISOC = 1;
+ public static final int USB_ENDPOINT_XFER_ISOC =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_ISOC; // 1
/**
* Bulk endpoint type
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_BULK = 2;
+ public static final int USB_ENDPOINT_XFER_BULK =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_BULK; // 2
/**
* Interrupt endpoint type
* @see UsbEndpoint#getType
*/
- public static final int USB_ENDPOINT_XFER_INT = 3;
+ public static final int USB_ENDPOINT_XFER_INT =
+ ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_INT; // 3
/**
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 31a3096..ecccda5 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -18,9 +18,6 @@
import android.annotation.SystemService;
import android.content.Context;
-import android.net.IEthernetManager;
-import android.net.IEthernetServiceListener;
-import android.net.IpConfiguration;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -45,18 +42,18 @@
if (msg.what == MSG_AVAILABILITY_CHANGED) {
boolean isAvailable = (msg.arg1 == 1);
for (Listener listener : mListeners) {
- listener.onAvailabilityChanged(isAvailable);
+ listener.onAvailabilityChanged((String) msg.obj, isAvailable);
}
}
}
};
- private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+ private final ArrayList<Listener> mListeners = new ArrayList<>();
private final IEthernetServiceListener.Stub mServiceListener =
new IEthernetServiceListener.Stub() {
@Override
- public void onAvailabilityChanged(boolean isAvailable) {
+ public void onAvailabilityChanged(String iface, boolean isAvailable) {
mHandler.obtainMessage(
- MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
+ MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, iface).sendToTarget();
}
};
@@ -66,9 +63,10 @@
public interface Listener {
/**
* Called when Ethernet port's availability is changed.
- * @param isAvailable {@code true} if one or more Ethernet port exists.
+ * @param iface Ethernet interface name
+ * @param isAvailable {@code true} if Ethernet port exists.
*/
- public void onAvailabilityChanged(boolean isAvailable);
+ void onAvailabilityChanged(String iface, boolean isAvailable);
}
/**
@@ -86,9 +84,9 @@
* Get Ethernet configuration.
* @return the Ethernet Configuration, contained in {@link IpConfiguration}.
*/
- public IpConfiguration getConfiguration() {
+ public IpConfiguration getConfiguration(String iface) {
try {
- return mService.getConfiguration();
+ return mService.getConfiguration(iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -97,21 +95,29 @@
/**
* Set Ethernet configuration.
*/
- public void setConfiguration(IpConfiguration config) {
+ public void setConfiguration(String iface, IpConfiguration config) {
try {
- mService.setConfiguration(config);
+ mService.setConfiguration(iface, config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Indicates whether the system currently has one or more
- * Ethernet interfaces.
+ * Indicates whether the system currently has one or more Ethernet interfaces.
*/
public boolean isAvailable() {
+ return getAvailableInterfaces().length > 0;
+ }
+
+ /**
+ * Indicates whether the system has given interface.
+ *
+ * @param iface Ethernet interface name
+ */
+ public boolean isAvailable(String iface) {
try {
- return mService.isAvailable();
+ return mService.isAvailable(iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -137,6 +143,17 @@
}
/**
+ * Returns an array of available Ethernet interface names.
+ */
+ public String[] getAvailableInterfaces() {
+ try {
+ return mService.getAvailableInterfaces();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Removes a listener.
* @param listener A {@link Listener} to remove.
* @throws IllegalArgumentException If the listener is null.
diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl
index 7a92eb9..94960b5 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/core/java/android/net/IEthernetManager.aidl
@@ -26,9 +26,10 @@
/** {@hide} */
interface IEthernetManager
{
- IpConfiguration getConfiguration();
- void setConfiguration(in IpConfiguration config);
- boolean isAvailable();
+ String[] getAvailableInterfaces();
+ IpConfiguration getConfiguration(String iface);
+ void setConfiguration(String iface, in IpConfiguration config);
+ boolean isAvailable(String iface);
void addListener(in IEthernetServiceListener listener);
void removeListener(in IEthernetServiceListener listener);
}
diff --git a/core/java/android/net/IEthernetServiceListener.aidl b/core/java/android/net/IEthernetServiceListener.aidl
index 356690e8..782fa19 100644
--- a/core/java/android/net/IEthernetServiceListener.aidl
+++ b/core/java/android/net/IEthernetServiceListener.aidl
@@ -19,5 +19,5 @@
/** @hide */
oneway interface IEthernetServiceListener
{
- void onAvailabilityChanged(boolean isAvailable);
+ void onAvailabilityChanged(String iface, boolean isAvailable);
}
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 287bdc8..74d6470 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -26,6 +26,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;
@@ -329,16 +330,34 @@
/**
* Returns a generated MAC address whose 24 least significant bits constituting the
- * NIC part of the address are randomly selected.
+ * NIC part of the address are randomly selected and has Google OUI base.
*
* The locally assigned bit is always set to 1. The multicast bit is always set to 0.
*
- * @return a random locally assigned MacAddress.
+ * @return a random locally assigned, unicast MacAddress with Google OUI.
+ *
+ * @hide
+ */
+ public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
+ return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
+ }
+
+ /**
+ * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
+ * unicast bit, are randomly selected.
+ *
+ * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+ *
+ * @return a random locally assigned, unicast MacAddress.
*
* @hide
*/
public static @NonNull MacAddress createRandomUnicastAddress() {
- return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
+ SecureRandom r = new SecureRandom();
+ long addr = r.nextLong() & VALID_LONG_MASK;
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
+ return new MacAddress(addr);
}
/**
@@ -355,8 +374,8 @@
*/
public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
- addr = addr | LOCALLY_ASSIGNED_MASK;
- addr = addr & ~MULTICAST_MASK;
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
return new MacAddress(addr);
}
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 5df742c..9f47f62 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -42,6 +42,7 @@
public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
private static final int VERSION_INIT = 1;
private static final int VERSION_RULE = 2;
+ private static final int VERSION_RAPID = 3;
public static final int CYCLE_NONE = -1;
public static final long WARNING_DISABLED = -1;
@@ -54,6 +55,7 @@
public long limitBytes = LIMIT_DISABLED;
public long lastWarningSnooze = SNOOZE_NEVER;
public long lastLimitSnooze = SNOOZE_NEVER;
+ public long lastRapidSnooze = SNOOZE_NEVER;
@Deprecated public boolean metered = true;
public boolean inferred = false;
@@ -82,15 +84,24 @@
limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
+ @Deprecated
public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered,
boolean inferred) {
+ this(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze,
+ SNOOZE_NEVER, metered, inferred);
+ }
+
+ public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
+ long limitBytes, long lastWarningSnooze, long lastLimitSnooze, long lastRapidSnooze,
+ boolean metered, boolean inferred) {
this.template = Preconditions.checkNotNull(template, "missing NetworkTemplate");
this.cycleRule = Preconditions.checkNotNull(cycleRule, "missing RecurrenceRule");
this.warningBytes = warningBytes;
this.limitBytes = limitBytes;
this.lastWarningSnooze = lastWarningSnooze;
this.lastLimitSnooze = lastLimitSnooze;
+ this.lastRapidSnooze = lastRapidSnooze;
this.metered = metered;
this.inferred = inferred;
}
@@ -102,6 +113,7 @@
limitBytes = source.readLong();
lastWarningSnooze = source.readLong();
lastLimitSnooze = source.readLong();
+ lastRapidSnooze = source.readLong();
metered = source.readInt() != 0;
inferred = source.readInt() != 0;
}
@@ -114,6 +126,7 @@
dest.writeLong(limitBytes);
dest.writeLong(lastWarningSnooze);
dest.writeLong(lastLimitSnooze);
+ dest.writeLong(lastRapidSnooze);
dest.writeInt(metered ? 1 : 0);
dest.writeInt(inferred ? 1 : 0);
}
@@ -151,6 +164,7 @@
public void clearSnooze() {
lastWarningSnooze = SNOOZE_NEVER;
lastLimitSnooze = SNOOZE_NEVER;
+ lastRapidSnooze = SNOOZE_NEVER;
}
/**
@@ -176,7 +190,7 @@
@Override
public int hashCode() {
return Objects.hash(template, cycleRule, warningBytes, limitBytes,
- lastWarningSnooze, lastLimitSnooze, metered, inferred);
+ lastWarningSnooze, lastLimitSnooze, lastRapidSnooze, metered, inferred);
}
@Override
@@ -186,7 +200,9 @@
return warningBytes == other.warningBytes
&& limitBytes == other.limitBytes
&& lastWarningSnooze == other.lastWarningSnooze
- && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
+ && lastLimitSnooze == other.lastLimitSnooze
+ && lastRapidSnooze == other.lastRapidSnooze
+ && metered == other.metered
&& inferred == other.inferred
&& Objects.equals(template, other.template)
&& Objects.equals(cycleRule, other.cycleRule);
@@ -203,6 +219,7 @@
.append(" limitBytes=").append(limitBytes)
.append(" lastWarningSnooze=").append(lastWarningSnooze)
.append(" lastLimitSnooze=").append(lastLimitSnooze)
+ .append(" lastRapidSnooze=").append(lastRapidSnooze)
.append(" metered=").append(metered)
.append(" inferred=").append(inferred)
.append("}").toString();
@@ -224,13 +241,14 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
- out.writeInt(VERSION_RULE);
+ out.writeInt(VERSION_RAPID);
out.write(template.getBytesForBackup());
cycleRule.writeToStream(out);
out.writeLong(warningBytes);
out.writeLong(limitBytes);
out.writeLong(lastWarningSnooze);
out.writeLong(lastLimitSnooze);
+ out.writeLong(lastRapidSnooze);
out.writeInt(metered ? 1 : 0);
out.writeInt(inferred ? 1 : 0);
return baos.toByteArray();
@@ -239,35 +257,32 @@
public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException,
BackupUtils.BadVersionException {
final int version = in.readInt();
- switch (version) {
- case VERSION_INIT: {
- NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
- int cycleDay = in.readInt();
- String cycleTimeZone = BackupUtils.readString(in);
- long warningBytes = in.readLong();
- long limitBytes = in.readLong();
- long lastWarningSnooze = in.readLong();
- long lastLimitSnooze = in.readLong();
- boolean metered = in.readInt() == 1;
- boolean inferred = in.readInt() == 1;
- return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes,
- limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
- }
- case VERSION_RULE: {
- NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
- RecurrenceRule cycleRule = new RecurrenceRule(in);
- long warningBytes = in.readLong();
- long limitBytes = in.readLong();
- long lastWarningSnooze = in.readLong();
- long lastLimitSnooze = in.readLong();
- boolean metered = in.readInt() == 1;
- boolean inferred = in.readInt() == 1;
- return new NetworkPolicy(template, cycleRule, warningBytes,
- limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
- }
- default: {
- throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
- }
+ if (version > VERSION_RAPID) {
+ throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
}
+
+ final NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
+ final RecurrenceRule cycleRule;
+ if (version >= VERSION_RULE) {
+ cycleRule = new RecurrenceRule(in);
+ } else {
+ final int cycleDay = in.readInt();
+ final String cycleTimezone = BackupUtils.readString(in);
+ cycleRule = buildRule(cycleDay, ZoneId.of(cycleTimezone));
+ }
+ final long warningBytes = in.readLong();
+ final long limitBytes = in.readLong();
+ final long lastWarningSnooze = in.readLong();
+ final long lastLimitSnooze = in.readLong();
+ final long lastRapidSnooze;
+ if (version >= VERSION_RAPID) {
+ lastRapidSnooze = in.readLong();
+ } else {
+ lastRapidSnooze = SNOOZE_NEVER;
+ }
+ final boolean metered = in.readInt() == 1;
+ final boolean inferred = in.readInt() == 1;
+ return new NetworkPolicy(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze,
+ lastLimitSnooze, lastRapidSnooze, metered, inferred);
}
}
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 6758d95..3a40cf4 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,5 +1,4 @@
ek@google.com
-hugobenichi@google.com
jsharkey@android.com
jchalard@google.com
lorenzo@google.com
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bda720bb..7922276 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -439,6 +439,10 @@
}
}
+ private static long addIfSupported(long stat) {
+ return (stat == UNSUPPORTED) ? 0 : stat;
+ }
+
/**
* Return number of packets transmitted across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
@@ -451,7 +455,7 @@
public static long getMobileTxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxPackets(iface);
+ total += addIfSupported(getTxPackets(iface));
}
return total;
}
@@ -468,7 +472,7 @@
public static long getMobileRxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxPackets(iface);
+ total += addIfSupported(getRxPackets(iface));
}
return total;
}
@@ -485,7 +489,7 @@
public static long getMobileTxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxBytes(iface);
+ total += addIfSupported(getTxBytes(iface));
}
return total;
}
@@ -502,7 +506,7 @@
public static long getMobileRxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxBytes(iface);
+ total += addIfSupported(getRxBytes(iface));
}
return total;
}
@@ -517,9 +521,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
@@ -534,9 +536,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 9edcc0e..5ca3a41 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -720,6 +720,10 @@
LOOP: while (end < length) {
switch (uriString.charAt(end)) {
case '/': // Start of path
+ case '\\':// Start of path
+ // Per http://url.spec.whatwg.org/#host-state, the \ character
+ // is treated as if it were a / character when encountered in a
+ // host
case '?': // Start of query
case '#': // Start of fragment
break LOOP;
@@ -758,6 +762,10 @@
case '#': // Start of fragment
return ""; // Empty path.
case '/': // Start of path!
+ case '\\':// Start of path!
+ // Per http://url.spec.whatwg.org/#host-state, the \ character
+ // is treated as if it were a / character when encountered in a
+ // host
break LOOP;
}
pathStart++;
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 185b181..712576f 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -192,7 +192,7 @@
*/
public static Intent prepare(Context context) {
try {
- if (getService().prepareVpn(context.getPackageName(), null, UserHandle.myUserId())) {
+ if (getService().prepareVpn(context.getPackageName(), null, context.getUserId())) {
return null;
}
} catch (RemoteException e) {
@@ -219,7 +219,7 @@
String packageName = context.getPackageName();
try {
// Only prepare if we're not already prepared.
- int userId = UserHandle.myUserId();
+ int userId = context.getUserId();
if (!cm.prepareVpn(packageName, null, userId)) {
cm.prepareVpn(null, packageName, userId);
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index c7d4c65..958063a 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -393,7 +393,7 @@
"either scheme file or scheme content");
continue;
}
- uri = ContentProvider.maybeAddUserId(uri, UserHandle.myUserId());
+ uri = ContentProvider.maybeAddUserId(uri, activity.getUserId());
validUris.add(uri);
}
@@ -410,7 +410,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- return new BeamShareData(message, uris, new UserHandle(UserHandle.myUserId()), flags);
+ return new BeamShareData(message, uris, activity.getUser(), flags);
}
/** Callback from NFC service, usually on binder thread */
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index debef63..b02d48d 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -147,6 +147,19 @@
public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
/**
+ * Broadcast Action: Intent to notify an application that an transaction event has occurred
+ * on the Secure Element.
+ *
+ * <p>This intent will only be sent if the application has requested permission for
+ * {@link android.Manifest.permission#NFC_TRANSACTION_EVENT} and if the application has the
+ * necessary access to Secure Element which witnessed the particular event.
+ */
+ @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TRANSACTION_DETECTED =
+ "android.nfc.action.TRANSACTION_DETECTED";
+
+ /**
* Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
* @hide
*/
@@ -197,6 +210,23 @@
*/
public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
+ /**
+ * Mandatory byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+ */
+ public static final String EXTRA_AID = "android.nfc.extra.AID";
+
+ /**
+ * Optional byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+ */
+ public static final String EXTRA_DATA = "android.nfc.extra.DATA";
+
+ /**
+ * Mandatory String extra field in {@link #ACTION_TRANSACTION_DETECTED}
+ * Indicates the Secure Element on which the transaction occurred.
+ * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
+ */
+ public static final String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+
public static final int STATE_OFF = 1;
public static final int STATE_TURNING_ON = 2;
public static final int STATE_ON = 3;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 6dd7993..15d02f2 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -201,7 +201,7 @@
*/
public boolean isDefaultServiceForCategory(ComponentName service, String category) {
try {
- return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+ return sService.isDefaultServiceForCategory(mContext.getUserId(), service, category);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -210,7 +210,7 @@
return false;
}
try {
- return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
+ return sService.isDefaultServiceForCategory(mContext.getUserId(), service,
category);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover CardEmulationService.");
@@ -232,7 +232,7 @@
*/
public boolean isDefaultServiceForAid(ComponentName service, String aid) {
try {
- return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+ return sService.isDefaultServiceForAid(mContext.getUserId(), service, aid);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -241,7 +241,7 @@
return false;
}
try {
- return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+ return sService.isDefaultServiceForAid(mContext.getUserId(), service, aid);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
@@ -326,7 +326,7 @@
List<String> aids) {
AidGroup aidGroup = new AidGroup(aids, category);
try {
- return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
+ return sService.registerAidGroupForService(mContext.getUserId(), service, aidGroup);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -335,7 +335,7 @@
return false;
}
try {
- return sService.registerAidGroupForService(UserHandle.myUserId(), service,
+ return sService.registerAidGroupForService(mContext.getUserId(), service,
aidGroup);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -360,7 +360,7 @@
*/
public List<String> getAidsForService(ComponentName service, String category) {
try {
- AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
+ AidGroup group = sService.getAidGroupForService(mContext.getUserId(), service,
category);
return (group != null ? group.getAids() : null);
} catch (RemoteException e) {
@@ -370,7 +370,7 @@
return null;
}
try {
- AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
+ AidGroup group = sService.getAidGroupForService(mContext.getUserId(), service,
category);
return (group != null ? group.getAids() : null);
} catch (RemoteException ee) {
@@ -397,7 +397,7 @@
*/
public boolean removeAidsForService(ComponentName service, String category) {
try {
- return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
+ return sService.removeAidGroupForService(mContext.getUserId(), service, category);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -406,7 +406,7 @@
return false;
}
try {
- return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
+ return sService.removeAidGroupForService(mContext.getUserId(), service, category);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
@@ -537,7 +537,7 @@
*/
public boolean setDefaultServiceForCategory(ComponentName service, String category) {
try {
- return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+ return sService.setDefaultServiceForCategory(mContext.getUserId(), service, category);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -546,7 +546,7 @@
return false;
}
try {
- return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
+ return sService.setDefaultServiceForCategory(mContext.getUserId(), service,
category);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -560,7 +560,7 @@
*/
public boolean setDefaultForNextTap(ComponentName service) {
try {
- return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+ return sService.setDefaultForNextTap(mContext.getUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -569,7 +569,7 @@
return false;
}
try {
- return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+ return sService.setDefaultForNextTap(mContext.getUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
@@ -582,7 +582,7 @@
*/
public List<ApduServiceInfo> getServices(String category) {
try {
- return sService.getServices(UserHandle.myUserId(), category);
+ return sService.getServices(mContext.getUserId(), category);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -591,7 +591,7 @@
return null;
}
try {
- return sService.getServices(UserHandle.myUserId(), category);
+ return sService.getServices(mContext.getUserId(), category);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return null;
diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
index f2b5ebc..80e8579 100644
--- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -118,7 +118,7 @@
throw new NullPointerException("service is null");
}
try {
- return sService.getSystemCodeForService(UserHandle.myUserId(), service);
+ return sService.getSystemCodeForService(mContext.getUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -127,7 +127,7 @@
return null;
}
try {
- return sService.getSystemCodeForService(UserHandle.myUserId(), service);
+ return sService.getSystemCodeForService(mContext.getUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
@@ -164,7 +164,7 @@
throw new NullPointerException("service or systemCode is null");
}
try {
- return sService.registerSystemCodeForService(UserHandle.myUserId(),
+ return sService.registerSystemCodeForService(mContext.getUserId(),
service, systemCode);
} catch (RemoteException e) {
// Try one more time
@@ -174,7 +174,7 @@
return false;
}
try {
- return sService.registerSystemCodeForService(UserHandle.myUserId(),
+ return sService.registerSystemCodeForService(mContext.getUserId(),
service, systemCode);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -195,7 +195,7 @@
throw new NullPointerException("service is null");
}
try {
- return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
+ return sService.removeSystemCodeForService(mContext.getUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -204,7 +204,7 @@
return false;
}
try {
- return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
+ return sService.removeSystemCodeForService(mContext.getUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
@@ -230,7 +230,7 @@
throw new NullPointerException("service is null");
}
try {
- return sService.getNfcid2ForService(UserHandle.myUserId(), service);
+ return sService.getNfcid2ForService(mContext.getUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -239,7 +239,7 @@
return null;
}
try {
- return sService.getNfcid2ForService(UserHandle.myUserId(), service);
+ return sService.getNfcid2ForService(mContext.getUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
ee.rethrowAsRuntimeException();
@@ -273,7 +273,7 @@
throw new NullPointerException("service or nfcid2 is null");
}
try {
- return sService.setNfcid2ForService(UserHandle.myUserId(),
+ return sService.setNfcid2ForService(mContext.getUserId(),
service, nfcid2);
} catch (RemoteException e) {
// Try one more time
@@ -283,7 +283,7 @@
return false;
}
try {
- return sService.setNfcid2ForService(UserHandle.myUserId(),
+ return sService.setNfcid2ForService(mContext.getUserId(),
service, nfcid2);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -381,7 +381,7 @@
*/
public List<NfcFServiceInfo> getNfcFServices() {
try {
- return sService.getNfcFServices(UserHandle.myUserId());
+ return sService.getNfcFServices(mContext.getUserId());
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -390,7 +390,7 @@
return null;
}
try {
- return sService.getNfcFServices(UserHandle.myUserId());
+ return sService.getNfcFServices(mContext.getUserId());
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return null;
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index a734719..8b4f02e 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -323,4 +323,16 @@
public long getLongProperty(int id) {
return queryProperty(id);
}
+
+ /**
+ * Return true if the plugType given is wired
+ * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB},
+ * or {@link #BATTERY_PLUGGED_WIRELESS}
+ *
+ * @return true if plugType is wired
+ * @hide
+ */
+ public static boolean isPlugWired(int plugType) {
+ return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
+ }
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fd0e5ae..5d7cf1e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1537,6 +1537,7 @@
public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
public static final int STATE2_CAMERA_FLAG = 1<<21;
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
+ public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
public static final int MOST_INTERESTING_STATES2 =
STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2353,9 +2354,11 @@
WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+ new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG,
+ "cellular_high_tx_power", "Chtp"),
new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
- new String[] { "poor", "good"}, new String[] { "poor", "good"}),
+ new String[] { "poor", "good"}, new String[] { "poor", "good"})
};
public static final String[] HISTORY_EVENT_NAMES = new String[] {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 4aadc5b..682fdb7 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -784,7 +784,7 @@
private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
// Debuggable builds will throw an AssertionError if the number of map entries exceeds:
- private static final int CRASH_AT_SIZE = 5_000;
+ private static final int CRASH_AT_SIZE = 20_000;
/**
* We next warn when we exceed this bucket size.
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c53ec1..1160415 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,11 @@
package android.os;
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.provider.DocumentsContract.Document;
@@ -28,7 +33,9 @@
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.SizedInputStream;
+import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import java.io.BufferedInputStream;
@@ -41,10 +48,12 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -81,6 +90,14 @@
private static final File[] EMPTY = new File[0];
+ private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+
+ private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+ public interface ProgressListener {
+ public void onProgress(long progress);
+ }
+
/**
* Set owner and mode of of given {@link File}.
*
@@ -185,6 +202,9 @@
return false;
}
+ /**
+ * @deprecated use {@link #copy(File, File)} instead.
+ */
@Deprecated
public static boolean copyFile(File srcFile, File destFile) {
try {
@@ -195,14 +215,19 @@
}
}
- // copy a file from srcFile to destFile, return true if succeed, return
- // false if fail
+ /**
+ * @deprecated use {@link #copy(File, File)} instead.
+ */
+ @Deprecated
public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
try (InputStream in = new FileInputStream(srcFile)) {
copyToFileOrThrow(in, destFile);
}
}
+ /**
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ */
@Deprecated
public static boolean copyToFile(InputStream inputStream, File destFile) {
try {
@@ -214,32 +239,250 @@
}
/**
- * Copy data from a source stream to destFile.
- * Return true if succeed, return false if failed.
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
*/
- public static void copyToFileOrThrow(InputStream inputStream, File destFile)
- throws IOException {
+ @Deprecated
+ public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
if (destFile.exists()) {
destFile.delete();
}
- FileOutputStream out = new FileOutputStream(destFile);
- try {
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) >= 0) {
- out.write(buffer, 0, bytesRead);
- }
- } finally {
- out.flush();
+ try (FileOutputStream out = new FileOutputStream(destFile)) {
+ copy(in, out);
try {
- out.getFD().sync();
- } catch (IOException e) {
+ Os.fsync(out.getFD());
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
}
- out.close();
}
}
/**
+ * Copy the contents of one file to another, replacing any existing content.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull File from, @NonNull File to) throws IOException {
+ return copy(from, to, null, null);
+ }
+
+ /**
+ * Copy the contents of one file to another, replacing any existing content.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull File from, @NonNull File to,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ try (FileInputStream in = new FileInputStream(from);
+ FileOutputStream out = new FileOutputStream(to)) {
+ return copy(in, out, listener, signal);
+ }
+ }
+
+ /**
+ * Copy the contents of one stream to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
+ return copy(in, out, null, null);
+ }
+
+ /**
+ * Copy the contents of one stream to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+ return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+ listener, signal);
+ }
+ }
+
+ // Worse case fallback to userspace
+ return copyInternalUserspace(in, out, listener, signal);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
+ throws IOException {
+ return copy(in, out, null, null);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+ throws IOException {
+ return copy(in, out, listener, signal, Long.MAX_VALUE);
+ }
+
+ /**
+ * Copy the contents of one FD to another.
+ * <p>
+ * Attempts to use several optimization strategies to copy the data in the
+ * kernel before falling back to a userspace copy as a last resort.
+ *
+ * @param listener to be periodically notified as the copy progresses.
+ * @param signal to signal if the copy should be cancelled early.
+ * @param count the number of bytes to copy.
+ * @return number of bytes copied.
+ */
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count)
+ throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ try {
+ final StructStat st_in = Os.fstat(in);
+ final StructStat st_out = Os.fstat(out);
+ if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+ return copyInternalSendfile(in, out, listener, signal, count);
+ } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+ return copyInternalSplice(in, out, listener, signal, count);
+ }
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ }
+
+ // Worse case fallback to userspace
+ return copyInternalUserspace(in, out, listener, signal, count);
+ }
+
+ /**
+ * Requires one of input or output to be a pipe.
+ */
+ @VisibleForTesting
+ public static long copyInternalSplice(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES),
+ SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+ progress += t;
+ checkpoint += t;
+ count -= t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ /**
+ * Requires both input and output to be a regular file.
+ */
+ @VisibleForTesting
+ public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) {
+ progress += t;
+ checkpoint += t;
+ count -= t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ @VisibleForTesting
+ public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+ ProgressListener listener, CancellationSignal signal, long count) throws IOException {
+ if (count != Long.MAX_VALUE) {
+ return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
+ new FileOutputStream(out), listener, signal);
+ } else {
+ return copyInternalUserspace(new FileInputStream(in),
+ new FileOutputStream(out), listener, signal);
+ }
+ }
+
+ @VisibleForTesting
+ public static long copyInternalUserspace(InputStream in, OutputStream out,
+ ProgressListener listener, CancellationSignal signal) throws IOException {
+ long progress = 0;
+ long checkpoint = 0;
+ byte[] buffer = new byte[8192];
+
+ int t;
+ while ((t = in.read(buffer)) != -1) {
+ out.write(buffer, 0, t);
+
+ progress += t;
+ checkpoint += t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ return progress;
+ }
+
+ /**
* Check if a filename is "safe" (no metacharacters or spaces).
* @param file The file to check
*/
@@ -797,4 +1040,69 @@
}
return val * pow;
}
+
+ @VisibleForTesting
+ public static class MemoryPipe extends Thread implements AutoCloseable {
+ private final FileDescriptor[] pipe;
+ private final byte[] data;
+ private final boolean sink;
+
+ private MemoryPipe(byte[] data, boolean sink) throws IOException {
+ try {
+ this.pipe = Os.pipe();
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ this.data = data;
+ this.sink = sink;
+ }
+
+ private MemoryPipe startInternal() {
+ super.start();
+ return this;
+ }
+
+ public static MemoryPipe createSource(byte[] data) throws IOException {
+ return new MemoryPipe(data, false).startInternal();
+ }
+
+ public static MemoryPipe createSink(byte[] data) throws IOException {
+ return new MemoryPipe(data, true).startInternal();
+ }
+
+ public FileDescriptor getFD() {
+ return sink ? pipe[1] : pipe[0];
+ }
+
+ public FileDescriptor getInternalFD() {
+ return sink ? pipe[0] : pipe[1];
+ }
+
+ @Override
+ public void run() {
+ final FileDescriptor fd = getInternalFD();
+ try {
+ int i = 0;
+ while (i < data.length) {
+ if (sink) {
+ i += Os.read(fd, data, i, data.length - i);
+ } else {
+ i += Os.write(fd, data, i, data.length - i);
+ }
+ }
+ } catch (IOException | ErrnoException e) {
+ // Ignored
+ } finally {
+ if (sink) {
+ SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+ }
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ IoUtils.closeQuietly(getFD());
+ }
+ }
}
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index a565dee..fbdf27e 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -21,12 +21,6 @@
/** @hide */
@SystemApi
public interface IHwBinder {
- // These MUST match their corresponding libhwbinder/IBinder.h definition !!!
- /** @hide */
- public static final int FIRST_CALL_TRANSACTION = 1;
- /** @hide */
- public static final int FLAG_ONEWAY = 1;
-
/**
* Process a hwbinder transaction.
*
diff --git a/core/java/android/os/IServiceManager.java b/core/java/android/os/IServiceManager.java
index 2176a78..89bf7b9 100644
--- a/core/java/android/os/IServiceManager.java
+++ b/core/java/android/os/IServiceManager.java
@@ -76,9 +76,15 @@
int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0;
int DUMP_FLAG_PRIORITY_HIGH = 1 << 1;
int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2;
+ /**
+ * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the
+ * same priority as NORMAL priority but the services are not called with dump priority
+ * arguments.
+ */
+ int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3;
int DUMP_FLAG_PRIORITY_ALL = DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
- | DUMP_FLAG_PRIORITY_NORMAL;
+ | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT;
/* Allows services to dump sections in protobuf format. */
- int DUMP_FLAG_PROTO = 1 << 3;
+ int DUMP_FLAG_PROTO = 1 << 4;
}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 8a27700..eae5217 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -55,9 +55,8 @@
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
- /** Send a broadcast to the specified pkg and class that it should getData now. */
- // TODO: Rename this and use a pending intent instead.
- oneway void sendBroadcast(String pkg, String cls);
+ /** Send a broadcast to the specified PendingIntent's as IBinder that it should getData now. */
+ oneway void sendDataBroadcast(in IBinder intentSender);
/**
* Requests StatsCompanionService to send a broadcast using the given intentSender
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 679b49d..682a24f1 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -81,12 +81,26 @@
/**
* Sets a configuration with the specified config key and subscribes to updates for this
* configuration key. Broadcasts will be sent if this configuration needs to be collected.
- * The configuration must be a wire-encoded StatsdConfig. The caller specifies the name of the
- * package and class that should receive these broadcasts.
+ * The configuration must be a wire-encoded StatsDConfig. The receiver for this data is
+ * registered in a separate function.
*
* Returns if this configuration was correctly registered.
*/
- boolean addConfiguration(in long configKey, in byte[] config, in String pkg, in String cls);
+ boolean addConfiguration(in long configKey, in byte[] config);
+
+ /**
+ * Registers the given pending intent for this config key. This intent is invoked when the
+ * memory consumed by the metrics for this configuration approach the pre-defined limits. There
+ * can be at most one listener per config key.
+ *
+ * Returns if this listener was correctly registered.
+ */
+ boolean setDataFetchOperation(long configKey, in IBinder intentSender);
+
+ /**
+ * Removes the data fetch operation for the specified configuration.
+ */
+ boolean removeDataFetchOperation(long configKey);
/**
* Removes the configuration with the matching config key. No-op if this config key does not
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 42ec315..3be76d6 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -83,7 +83,7 @@
* @param service the service object
*/
public static void addService(String name, IBinder service) {
- addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_NORMAL);
+ addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
}
/**
@@ -96,7 +96,7 @@
* to access this service
*/
public static void addService(String name, IBinder service, boolean allowIsolated) {
- addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_NORMAL);
+ addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b2b43cf..7e7af1a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -393,8 +393,9 @@
public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
/**
- * Specifies if a user is disallowed from configuring location mode. Device owner and profile
- * owners can set this restriction and it only applies on the managed user.
+ * Specifies if a user is disallowed from enabling or disabling location providers. As a
+ * result, user is disallowed from turning on or off location. Device owner and profile owners
+ * can set this restriction and it only applies on the managed user.
*
* <p>In a managed profile, location sharing is forced off when it's off on primary user, so
* user can still turn off location sharing on managed profile when the restriction is set by
@@ -408,11 +409,12 @@
*
* <p>Key for user restrictions.
* <p>Type: Boolean
+ * @see android.location.LocationManager#isProviderEnabled(String)
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- public static final String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+ public static final String DISALLOW_CONFIG_LOCATION = "no_config_location";
/**
* Specifies if date, time and timezone configuring is disallowed.
@@ -941,6 +943,20 @@
* @see #getUserRestrictions()
*/
public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
+
+ /**
+ * Specifies whether the user is allowed to print.
+ *
+ * This restriction can be set by device or profile owner.
+ *
+ * The default value is {@code false}.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_PRINTING = "no_printing";
+
/**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 12a495b..fb22194 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -80,4 +80,11 @@
* ("28", ["libjpeg.so", "libbase.so"])]
*/
public static native Map<String, String[]> getVndkSnapshots();
+
+ /**
+ * @return target FCM version, a number specified in the device manifest
+ * indicating the FCM version that the device manifest implements. Null if
+ * device manifest doesn't specify this number (for legacy devices).
+ */
+ public static native Long getTargetFrameworkCompatibilityMatrixVersion();
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f4deeed..f593e80 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1096,7 +1096,7 @@
public @NonNull List<StorageVolume> getStorageVolumes() {
final ArrayList<StorageVolume> res = new ArrayList<>();
Collections.addAll(res,
- getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
+ getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
return res;
}
@@ -1107,7 +1107,7 @@
* {@link Context#getExternalFilesDir(String)}.
*/
public @NonNull StorageVolume getPrimaryStorageVolume() {
- return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
+ return getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
}
/** {@hide} */
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index 747400d..a5f9305 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -21,6 +21,8 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
+import android.os.FileUtils;
+import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -114,28 +116,15 @@
@Override
protected Void doInBackground(Void... params) {
- InputStream in = null;
- OutputStream out = new FileOutputStream(mDestination.getFileDescriptor());
- final byte[] buffer = new byte[8192];
- try {
- in = new FileInputStream(mFile);
- while (true) {
- if (isCancelled()) {
- break;
- }
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error writing data!", ioe);
- mResultCallback.onWriteFailed(mContext.getString(
- R.string.write_fail_reason_cannot_write));
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
+ try (InputStream in = new FileInputStream(mFile);
+ OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) {
+ FileUtils.copy(in, out, null, mCancellationSignal);
+ } catch (OperationCanceledException e) {
+ // Ignored; already handled below
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Error writing data!", e);
+ mResultCallback.onWriteFailed(mContext.getString(
+ R.string.write_fail_reason_cannot_write));
}
return null;
}
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
index 2cd1d48..362eba9 100644
--- a/core/java/android/provider/ContactsInternal.java
+++ b/core/java/android/provider/ContactsInternal.java
@@ -68,7 +68,7 @@
}
}
// Launch on the current profile.
- startQuickContactWithErrorToastForUser(context, intent, Process.myUserHandle());
+ startQuickContactWithErrorToastForUser(context, intent, context.getUser());
}
public static void startQuickContactWithErrorToastForUser(Context context, Intent intent,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1f0d683..84996e0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1903,7 +1903,7 @@
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
String newValue = getStringForUser(cr, name, userHandle);
StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
- makeDefault ? 1 : 0, userHandle);
+ makeDefault, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -2255,7 +2255,7 @@
* @return the corresponding value, or null if not present
*/
public static String getString(ContentResolver resolver, String name) {
- return getStringForUser(resolver, name, UserHandle.myUserId());
+ return getStringForUser(resolver, name, resolver.getUserId());
}
/** @hide */
@@ -2282,7 +2282,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putString(ContentResolver resolver, String name, String value) {
- return putStringForUser(resolver, name, value, UserHandle.myUserId());
+ return putStringForUser(resolver, name, value, resolver.getUserId());
}
/** @hide */
@@ -2336,7 +2336,7 @@
* or not a valid integer.
*/
public static int getInt(ContentResolver cr, String name, int def) {
- return getIntForUser(cr, name, def, UserHandle.myUserId());
+ return getIntForUser(cr, name, def, cr.getUserId());
}
/** @hide */
@@ -2369,7 +2369,7 @@
*/
public static int getInt(ContentResolver cr, String name)
throws SettingNotFoundException {
- return getIntForUser(cr, name, UserHandle.myUserId());
+ return getIntForUser(cr, name, cr.getUserId());
}
/** @hide */
@@ -2397,7 +2397,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putInt(ContentResolver cr, String name, int value) {
- return putIntForUser(cr, name, value, UserHandle.myUserId());
+ return putIntForUser(cr, name, value, cr.getUserId());
}
/** @hide */
@@ -2421,7 +2421,7 @@
* or not a valid {@code long}.
*/
public static long getLong(ContentResolver cr, String name, long def) {
- return getLongForUser(cr, name, def, UserHandle.myUserId());
+ return getLongForUser(cr, name, def, cr.getUserId());
}
/** @hide */
@@ -2456,7 +2456,7 @@
*/
public static long getLong(ContentResolver cr, String name)
throws SettingNotFoundException {
- return getLongForUser(cr, name, UserHandle.myUserId());
+ return getLongForUser(cr, name, cr.getUserId());
}
/** @hide */
@@ -2484,7 +2484,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putLong(ContentResolver cr, String name, long value) {
- return putLongForUser(cr, name, value, UserHandle.myUserId());
+ return putLongForUser(cr, name, value, cr.getUserId());
}
/** @hide */
@@ -2508,7 +2508,7 @@
* or not a valid float.
*/
public static float getFloat(ContentResolver cr, String name, float def) {
- return getFloatForUser(cr, name, def, UserHandle.myUserId());
+ return getFloatForUser(cr, name, def, cr.getUserId());
}
/** @hide */
@@ -2542,7 +2542,7 @@
*/
public static float getFloat(ContentResolver cr, String name)
throws SettingNotFoundException {
- return getFloatForUser(cr, name, UserHandle.myUserId());
+ return getFloatForUser(cr, name, cr.getUserId());
}
/** @hide */
@@ -2573,7 +2573,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putFloat(ContentResolver cr, String name, float value) {
- return putFloatForUser(cr, name, value, UserHandle.myUserId());
+ return putFloatForUser(cr, name, value, cr.getUserId());
}
/** @hide */
@@ -2591,7 +2591,7 @@
* @param outConfig Where to place the configuration settings.
*/
public static void getConfiguration(ContentResolver cr, Configuration outConfig) {
- adjustConfigurationForUser(cr, outConfig, UserHandle.myUserId(),
+ adjustConfigurationForUser(cr, outConfig, cr.getUserId(),
false /* updateSettingsIfEmpty */);
}
@@ -2644,7 +2644,7 @@
* @return true if the values were set, false on database errors
*/
public static boolean putConfiguration(ContentResolver cr, Configuration config) {
- return putConfigurationForUser(cr, config, UserHandle.myUserId());
+ return putConfigurationForUser(cr, config, cr.getUserId());
}
/** @hide */
@@ -2664,7 +2664,7 @@
/** @deprecated - Do not use */
@Deprecated
public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
- return getShowGTalkServiceStatusForUser(cr, UserHandle.myUserId());
+ return getShowGTalkServiceStatusForUser(cr, cr.getUserId());
}
/**
@@ -2680,7 +2680,7 @@
/** @deprecated - Do not use */
@Deprecated
public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
- setShowGTalkServiceStatusForUser(cr, flag, UserHandle.myUserId());
+ setShowGTalkServiceStatusForUser(cr, flag, cr.getUserId());
}
/**
@@ -3692,6 +3692,20 @@
new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
/**
+ * User-selected RTT mode
+ * 0 = OFF
+ * 1 = FULL
+ * 2 = VCO
+ * 3 = HCO
+ * Uses the same constants as TTY (e.g. {@link android.telecom.TelecomManager#TTY_MODE_OFF})
+ * @hide
+ */
+ public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+
+ /** @hide */
+ public static final Validator RTT_CALLING_MODE_VALIDATOR = TTY_MODE_VALIDATOR;
+
+ /**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
* boolean (1 or 0).
*/
@@ -4016,6 +4030,7 @@
DTMF_TONE_WHEN_DIALING,
DTMF_TONE_TYPE_WHEN_DIALING,
HEARING_AID,
+ RTT_CALLING_MODE,
TTY_MODE,
MASTER_MONO,
SOUND_EFFECTS_ENABLED,
@@ -4214,6 +4229,7 @@
VALIDATORS.put(DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR);
VALIDATORS.put(HEARING_AID, HEARING_AID_VALIDATOR);
VALIDATORS.put(TTY_MODE, TTY_MODE_VALIDATOR);
+ VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, NOTIFICATION_LIGHT_PULSE_VALIDATOR);
VALIDATORS.put(POINTER_LOCATION, POINTER_LOCATION_VALIDATOR);
VALIDATORS.put(SHOW_TOUCHES, SHOW_TOUCHES_VALIDATOR);
@@ -4732,7 +4748,7 @@
* @return the corresponding value, or null if not present
*/
public static String getString(ContentResolver resolver, String name) {
- return getStringForUser(resolver, name, UserHandle.myUserId());
+ return getStringForUser(resolver, name, resolver.getUserId());
}
/** @hide */
@@ -4786,7 +4802,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putString(ContentResolver resolver, String name, String value) {
- return putStringForUser(resolver, name, value, UserHandle.myUserId());
+ return putStringForUser(resolver, name, value, resolver.getUserId());
}
/** @hide */
@@ -4859,7 +4875,7 @@
@NonNull String name, @Nullable String value, @Nullable String tag,
boolean makeDefault) {
return putStringForUser(resolver, name, value, tag, makeDefault,
- UserHandle.myUserId());
+ resolver.getUserId());
}
/**
@@ -4880,7 +4896,7 @@
public static void resetToDefaults(@NonNull ContentResolver resolver,
@Nullable String tag) {
resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS,
- UserHandle.myUserId());
+ resolver.getUserId());
}
/**
@@ -4947,7 +4963,7 @@
* or not a valid integer.
*/
public static int getInt(ContentResolver cr, String name, int def) {
- return getIntForUser(cr, name, def, UserHandle.myUserId());
+ return getIntForUser(cr, name, def, cr.getUserId());
}
/** @hide */
@@ -4984,7 +5000,7 @@
*/
public static int getInt(ContentResolver cr, String name)
throws SettingNotFoundException {
- return getIntForUser(cr, name, UserHandle.myUserId());
+ return getIntForUser(cr, name, cr.getUserId());
}
/** @hide */
@@ -5016,7 +5032,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putInt(ContentResolver cr, String name, int value) {
- return putIntForUser(cr, name, value, UserHandle.myUserId());
+ return putIntForUser(cr, name, value, cr.getUserId());
}
/** @hide */
@@ -5040,7 +5056,7 @@
* or not a valid {@code long}.
*/
public static long getLong(ContentResolver cr, String name, long def) {
- return getLongForUser(cr, name, def, UserHandle.myUserId());
+ return getLongForUser(cr, name, def, cr.getUserId());
}
/** @hide */
@@ -5075,7 +5091,7 @@
*/
public static long getLong(ContentResolver cr, String name)
throws SettingNotFoundException {
- return getLongForUser(cr, name, UserHandle.myUserId());
+ return getLongForUser(cr, name, cr.getUserId());
}
/** @hide */
@@ -5103,7 +5119,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putLong(ContentResolver cr, String name, long value) {
- return putLongForUser(cr, name, value, UserHandle.myUserId());
+ return putLongForUser(cr, name, value, cr.getUserId());
}
/** @hide */
@@ -5127,7 +5143,7 @@
* or not a valid float.
*/
public static float getFloat(ContentResolver cr, String name, float def) {
- return getFloatForUser(cr, name, def, UserHandle.myUserId());
+ return getFloatForUser(cr, name, def, cr.getUserId());
}
/** @hide */
@@ -5161,7 +5177,7 @@
*/
public static float getFloat(ContentResolver cr, String name)
throws SettingNotFoundException {
- return getFloatForUser(cr, name, UserHandle.myUserId());
+ return getFloatForUser(cr, name, cr.getUserId());
}
/** @hide */
@@ -5192,7 +5208,7 @@
* @return true if the value was set, false on database errors
*/
public static boolean putFloat(ContentResolver cr, String name, float value) {
- return putFloatForUser(cr, name, value, UserHandle.myUserId());
+ return putFloatForUser(cr, name, value, cr.getUserId());
}
/** @hide */
@@ -7380,7 +7396,8 @@
*/
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
- private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+ private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
/**
* Control the color temperature of Night Display, represented in Kelvin.
@@ -7999,7 +8016,7 @@
*/
@Deprecated
public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
- return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId());
+ return isLocationProviderEnabledForUser(cr, provider, cr.getUserId());
}
/**
@@ -8031,7 +8048,7 @@
@Deprecated
public static final void setLocationProviderEnabled(ContentResolver cr,
String provider, boolean enabled) {
- setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId());
+ setLocationProviderEnabledForUser(cr, provider, enabled, cr.getUserId());
}
/**
@@ -8373,10 +8390,10 @@
private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * URI for the "wireless charging started" sound.
+ * URI for the "wireless charging started" and "wired charging started" sound.
* @hide
*/
- public static final String WIRELESS_CHARGING_STARTED_SOUND =
+ public static final String CHARGING_STARTED_SOUND =
"wireless_charging_started_sound";
/**
@@ -9315,17 +9332,6 @@
private static final Validator WIFI_WAKEUP_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * Value to specify if Wi-Fi Wakeup is available.
- *
- * Wi-Fi Wakeup will only operate if it's available
- * and {@link #WIFI_WAKEUP_ENABLED} is true.
- *
- * Type: int (0 for false, 1 for true)
- * @hide
- */
- public static final String WIFI_WAKEUP_AVAILABLE = "wifi_wakeup_available";
-
- /**
* Value to specify whether network quality scores and badging should be shown in the UI.
*
* Type: int (0 for false, 1 for true)
@@ -10266,6 +10272,20 @@
public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants";
/**
+ * An integer to show the version of the anomaly config. Ex: 1, which means
+ * current version is 1.
+ * @hide
+ */
+ public static final String ANOMALY_CONFIG_VERSION = "anomaly_config_version";
+
+ /**
+ * A base64-encoded string represents anomaly stats config, used for
+ * {@link android.app.StatsManager}.
+ * @hide
+ */
+ public static final String ANOMALY_CONFIG = "anomaly_config";
+
+ /**
* Always on display(AOD) specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -10304,6 +10324,15 @@
public static final String FPS_DEVISOR = "fps_divisor";
/**
+ * Flag to enable or disable display panel low power mode (lpm)
+ * false -> Display panel power saving mode is disabled.
+ * true -> Display panel power saving mode is enabled.
+ *
+ * @hide
+ */
+ public static final String DISPLAY_PANEL_LPM = "display_panel_lpm";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
* <p>
@@ -10750,14 +10779,14 @@
public static final String LOW_POWER_MODE = "low_power";
/**
- * Battery level [1-99] at which low power mode automatically turns on.
+ * Battery level [1-100] at which low power mode automatically turns on.
* If 0, it will not automatically turn on.
* @hide
*/
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 99);
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
/**
* If not 0, the activity manager will aggressively finish activities and
@@ -11344,6 +11373,15 @@
"chained_battery_attribution_enabled";
/**
+ * The packages whitelisted to be run in autofill compatibility mode.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String AUTOFILL_COMPAT_ALLOWED_PACKAGES =
+ "autofill_compat_allowed_packages";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -11478,7 +11516,7 @@
* @return the corresponding value, or null if not present
*/
public static String getString(ContentResolver resolver, String name) {
- return getStringForUser(resolver, name, UserHandle.myUserId());
+ return getStringForUser(resolver, name, resolver.getUserId());
}
/** @hide */
@@ -11501,7 +11539,7 @@
*/
public static boolean putString(ContentResolver resolver,
String name, String value) {
- return putStringForUser(resolver, name, value, null, false, UserHandle.myUserId());
+ return putStringForUser(resolver, name, value, null, false, resolver.getUserId());
}
/**
@@ -11550,7 +11588,7 @@
@NonNull String name, @Nullable String value, @Nullable String tag,
boolean makeDefault) {
return putStringForUser(resolver, name, value, tag, makeDefault,
- UserHandle.myUserId());
+ resolver.getUserId());
}
/**
@@ -11571,7 +11609,7 @@
public static void resetToDefaults(@NonNull ContentResolver resolver,
@Nullable String tag) {
resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS,
- UserHandle.myUserId());
+ resolver.getUserId());
}
/**
@@ -11885,8 +11923,6 @@
*/
public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt";
-
-
/** User preferred subscriptions setting.
* This holds the details of the user selected subscription from the card and
* the activation status. Each settings string have the comma separated values
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index f0b9fa2d..65ce67f 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.io.IOException;
@@ -168,8 +169,10 @@
throw new IOException("Error in communicating with Secure Element");
}
return response;
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
}
}
}
@@ -244,8 +247,10 @@
synchronized (mLock) {
return mChannel.selectNext();
}
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
}
}
}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 9f15739..3dec976 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.io.IOException;
@@ -45,8 +46,7 @@
private final Object mLock = new Object();
- Reader(SEService service, String name, ISecureElementReader reader) throws
- IOException {
+ Reader(SEService service, String name, ISecureElementReader reader) {
if (reader == null || service == null || name == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
@@ -96,8 +96,10 @@
ISecureElementSession session;
try {
session = mReader.openSession();
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
}
if (session == null) {
throw new IOException("service session is null.");
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index 1e37277d..b8937e6 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -42,6 +42,23 @@
*/
public class SEService {
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if there was an error communicating with the Secure Element.
+ *
+ * @hide
+ */
+ public static final int IO_ERROR = 1;
+
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if AID cannot be selected or is not available when opening
+ * a logical channel.
+ *
+ * @hide
+ */
+ public static final int NO_SUCH_ELEMENT_ERROR = 2;
+
private static final String TAG = "OMAPI.SEService";
private final Object mLock = new Object();
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index bb2a032..19a018e 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.io.IOException;
@@ -207,8 +208,16 @@
return null;
}
return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
} catch (RemoteException e) {
- throw new IOException(e.getMessage());
+ throw new IllegalStateException(e.getMessage());
}
}
}
@@ -311,8 +320,16 @@
return null;
}
return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
} catch (RemoteException e) {
- throw new IOException(e.getMessage());
+ throw new IllegalStateException(e.getMessage());
}
}
}
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 4e4a037..7cd08f7 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,9 +26,13 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
import com.android.internal.widget.ILockSettings;
+import java.security.Key;
+import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
@@ -113,9 +117,11 @@
private final ILockSettings mBinder;
+ private final KeyStore mKeyStore;
- private RecoveryController(ILockSettings binder) {
+ private RecoveryController(ILockSettings binder, KeyStore keystore) {
mBinder = binder;
+ mKeyStore = keystore;
}
/**
@@ -133,7 +139,7 @@
public static RecoveryController getInstance(Context context) {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryController(lockSettings);
+ return new RecoveryController(lockSettings, KeyStore.getInstance());
}
/**
@@ -430,6 +436,7 @@
}
/**
+ * Deprecated.
* Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
* key store. Returns the raw material of the key.
*
@@ -444,7 +451,6 @@
public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
throws InternalRecoveryServiceException, LockScreenRequiredException {
try {
- // TODO: add account
return mBinder.generateAndStoreKey(alias);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -457,6 +463,72 @@
}
/**
+ * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
+ * key store. Returns {@link javax.crypto.SecretKey}.
+ *
+ * @param alias The key alias.
+ * @param account The account associated with the key.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
+ * to generate recoverable keys, as the snapshots are encrypted using a key derived from the
+ * lock screen.
+ * @hide
+ */
+ public Key generateKey(@NonNull String alias, byte[] account)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ // TODO: update RecoverySession.recoverKeys
+ try {
+ String grantAlias = mBinder.generateKey(alias, account);
+ if (grantAlias == null) {
+ return null;
+ }
+ Key result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (UnrecoverableKeyException e) {
+ throw new InternalRecoveryServiceException("Access to newly generated key failed for");
+ } 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.
+ *
+ * @param alias The key alias.
+ * @return The key.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws UnrecoverableKeyException if key is permanently invalidated or not found.
+ * @hide
+ */
+ public @Nullable Key getKey(@NonNull String alias)
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ try {
+ String grantAlias = mBinder.getKey(alias);
+ if (grantAlias == null) {
+ return null;
+ }
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Removes a key called {@code alias} from the recoverable key store.
*
* @param alias The key alias.
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 12aa64e..7a304c7 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -490,8 +490,45 @@
* strong suspicious that it could. For example, if an activity has four or more fields and one of
* them is a list, chances are that these are address fields (like address, city, state, and
* zip code).
+ *
+ * <a name="CompatibilityMode"></a>
+ * <h3>Compatibility mode</h3>
+ *
+ * <p>Apps that use standard Android widgets support autofill out-of-the-box and need to do
+ * very little to improve their user experience (annotating autofillable views and providing
+ * autofill hints). However, some apps do their own rendering and the rendered content may
+ * contain semantic structure that needs to be surfaced to the autofill framework. The platform
+ * exposes APIs to achieve this, however it could take some time until these apps implement
+ * autofill support.
+ *
+ * <p>To enable autofill for such apps the platform provides a compatibility mode in which the
+ * platform would fall back to the accessibility APIs to generate the state reported to autofill
+ * services and fill data. This mode needs to be explicitly requested for a given package up
+ * to a specified max version code allowing clean migration path when the target app begins to
+ * support autofill natively. Note that enabling compatibility may degrade performance for the
+ * target package and should be used with caution. The platform supports whitelisting which packages
+ * can be targeted in compatibility mode to ensure this mode is used only when needed and as long
+ * as needed.
+ *
+ * <p>You can request compatibility mode for packages of interest in the meta-data resource
+ * associated with your service. Below is a sample service declaration:
+ *
+ * <pre> <service android:name=".MyAutofillService"
+ * android:permission="android.permission.BIND_AUTOFILL_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.autofill.AutofillService" />
+ * </intent-filter>
+ * <meta-data android:name="android.autofillservice" android:resource="@xml/autofillservice" />
+ * </service></pre>
+ *
+ * <P>In the XML file you can specify one or more packages for which to enable compatibility
+ * mode. Below is a sample meta-data declaration:
+ *
+ * <pre> <autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
+ * <compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/>
+ * </autofill-service></pre>
*/
-// TODO(b/70407264): add code snippets above???
+// TODO(b/70407264): add code snippets to field classification ???
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 4f2f6cb..7383de7 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
@@ -27,6 +28,8 @@
import android.content.res.XmlResourceParser;
import android.metrics.LogMaker;
import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -35,11 +38,14 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
/**
* {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -49,6 +55,9 @@
public final class AutofillServiceInfo {
private static final String TAG = "AutofillServiceInfo";
+ private static final String TAG_AUTOFILL_SERVICE = "autofill-service";
+ private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package";
+
private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
try {
@@ -70,29 +79,15 @@
@Nullable
private final String mSettingsActivity;
- public AutofillServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
- throws PackageManager.NameNotFoundException {
- this(pm, getServiceInfoOrThrow(comp, userHandle));
- }
-
- public AutofillServiceInfo(PackageManager pm, ServiceInfo si) {
- mServiceInfo = si;
- final TypedArray metaDataArray = getMetaDataArray(pm, si);
- if (metaDataArray != null) {
- mSettingsActivity = metaDataArray
- .getString(R.styleable.AutofillService_settingsActivity);
- metaDataArray.recycle();
- } else {
- mSettingsActivity = null;
- }
- }
-
- /**
- * Gets the meta-data as a {@link TypedArray}, or {@code null} if not provided,
- * or throws if invalid.
- */
@Nullable
- private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
+ private final Map<String, Long> mCompatibilityPackages;
+
+ public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
+ throws PackageManager.NameNotFoundException {
+ this(context, getServiceInfoOrThrow(comp, userHandle));
+ }
+
+ public AutofillServiceInfo(Context context, ServiceInfo si) {
// Check for permissions.
if (!Manifest.permission.BIND_AUTOFILL_SERVICE.equals(si.permission)) {
if (Manifest.permission.BIND_AUTOFILL.equals(si.permission)) {
@@ -111,45 +106,115 @@
}
}
+ mServiceInfo = si;
+
// Get the AutoFill metadata, if declared.
- XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA);
+ final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
+ AutofillService.SERVICE_META_DATA);
if (parser == null) {
- return null;
+ mSettingsActivity = null;
+ mCompatibilityPackages = null;
+ return;
}
- // Parse service info and get the <autofill-service> tag as an AttributeSet.
- AttributeSet attrs;
+ String settingsActivity = null;
+ Map<String, Long> compatibilityPackages = null;
+
try {
- // Move the XML parser to the first tag.
- try {
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
+ final Resources resources = context.getPackageManager().getResourcesForApplication(
+ si.applicationInfo);
+
+ int type = 0;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ if (TAG_AUTOFILL_SERVICE.equals(parser.getName())) {
+ final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+ TypedArray afsAttributes = null;
+ try {
+ afsAttributes = resources.obtainAttributes(allAttributes,
+ com.android.internal.R.styleable.AutofillService);
+ settingsActivity = afsAttributes.getString(
+ R.styleable.AutofillService_settingsActivity);
+ } finally {
+ if (afsAttributes != null) {
+ afsAttributes.recycle();
+ }
}
- } catch (XmlPullParserException | IOException e) {
- Log.e(TAG, "Error parsing auto fill service meta-data", e);
- return null;
- }
-
- if (!"autofill-service".equals(parser.getName())) {
+ compatibilityPackages = parseCompatibilityPackages(parser, resources);
+ } else {
Log.e(TAG, "Meta-data does not start with autofill-service tag");
- return null;
}
- attrs = Xml.asAttributeSet(parser);
-
- // Get resources required to read the AttributeSet.
- Resources res;
- try {
- res = pm.getResourcesForApplication(si.applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Error getting application resources", e);
- return null;
- }
-
- return res.obtainAttributes(attrs, R.styleable.AutofillService);
- } finally {
- parser.close();
+ } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+ Log.e(TAG, "Error parsing auto fill service meta-data", e);
}
+
+ mSettingsActivity = settingsActivity;
+ mCompatibilityPackages = compatibilityPackages;
+ }
+
+ private Map<String, Long> parseCompatibilityPackages(XmlPullParser parser, Resources resources)
+ throws IOException, XmlPullParserException {
+ Map<String, Long> compatibilityPackages = null;
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (TAG_COMPATIBILITY_PACKAGE.equals(parser.getName())) {
+ TypedArray cpAttributes = null;
+ try {
+ final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+
+ cpAttributes = resources.obtainAttributes(allAttributes,
+ R.styleable.AutofillService_CompatibilityPackage);
+
+ final String name = cpAttributes.getString(
+ R.styleable.AutofillService_CompatibilityPackage_name);
+ if (TextUtils.isEmpty(name)) {
+ Log.e(TAG, "Invalid compatibility package:" + name);
+ break;
+ }
+
+ final String maxVersionCodeStr = cpAttributes.getString(
+ R.styleable.AutofillService_CompatibilityPackage_maxLongVersionCode);
+ final Long maxVersionCode;
+ if (maxVersionCodeStr != null) {
+ try {
+ maxVersionCode = Long.parseLong(maxVersionCodeStr);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Invalid compatibility max version code:"
+ + maxVersionCodeStr);
+ break;
+ }
+ if (maxVersionCode < 0) {
+ Log.e(TAG, "Invalid compatibility max version code:"
+ + maxVersionCode);
+ break;
+ }
+ } else {
+ maxVersionCode = Long.MAX_VALUE;
+ }
+
+ if (compatibilityPackages == null) {
+ compatibilityPackages = new ArrayMap<>();
+ }
+ compatibilityPackages.put(name, maxVersionCode);
+ } finally {
+ XmlUtils.skipCurrentTag(parser);
+ if (cpAttributes != null) {
+ cpAttributes.recycle();
+ }
+ }
+ }
+ }
+
+ return compatibilityPackages;
}
public ServiceInfo getServiceInfo() {
@@ -161,8 +226,35 @@
return mSettingsActivity;
}
+ @Nullable
+ public boolean isCompatibilityModeRequested(String packageName, long versionCode) {
+ if (mCompatibilityPackages == null) {
+ return false;
+ }
+ final Long maxVersionCode = mCompatibilityPackages.get(packageName);
+ if (maxVersionCode == null) {
+ return false;
+ }
+ return versionCode <= maxVersionCode;
+ }
+
@Override
public String toString() {
- return mServiceInfo == null ? "null" : mServiceInfo.toString();
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append("[").append(mServiceInfo);
+ builder.append(", settings:").append(mSettingsActivity);
+ builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null
+ && !mCompatibilityPackages.isEmpty()).append("]");
+ return builder.toString();
+ }
+
+ /**
+ * Dumps it!
+ */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName());
+ pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity);
+ pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages);
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index b7b2b2d..422e36b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1343,6 +1343,7 @@
/**
* @hide
*/
+ @GuardedBy("mLock")
public final void applyUpdateLocked(NotificationRankingUpdate update) {
mRankingMap = new RankingMap(update);
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 23ae4b9..d66322c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -17,6 +17,7 @@
package android.service.notification;
import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -1445,4 +1446,99 @@
return !config.allowAlarms && !config.allowMediaSystemOther
&& areAllPriorityOnlyNotificationZenSoundsMuted(config);
}
+
+ /**
+ * Returns a description of the current do not disturb settings from config.
+ * - If turned on manually and end time is known, returns end time.
+ * - If turned on by an automatic rule, returns the automatic rule name.
+ * - If on due to an app, returns the app name.
+ * - If there's a combination of rules/apps that trigger, then shows the one that will
+ * last the longest if applicable.
+ * @return null if do not disturb is off.
+ */
+ public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) {
+ if (!zenOn) {
+ return null;
+ }
+
+ String secondaryText = "";
+ long latestEndTime = -1;
+
+ // DND turned on by manual rule
+ if (config.manualRule != null) {
+ final Uri id = config.manualRule.conditionId;
+ if (config.manualRule.enabler != null) {
+ // app triggered manual rule
+ String appName = getOwnerCaption(context, config.manualRule.enabler);
+ if (!appName.isEmpty()) {
+ secondaryText = appName;
+ }
+ } else {
+ if (id == null) {
+ // Do not disturb manually triggered to remain on forever until turned off
+ // No subtext
+ return null;
+ } else {
+ latestEndTime = tryParseCountdownConditionId(id);
+ if (latestEndTime > 0) {
+ final CharSequence formattedTime = getFormattedTime(context,
+ latestEndTime, isToday(latestEndTime),
+ context.getUserId());
+ secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
+ }
+ }
+ }
+ }
+
+ // DND turned on by an automatic rule
+ for (ZenRule automaticRule : config.automaticRules.values()) {
+ if (automaticRule.isAutomaticActive()) {
+ if (isValidEventConditionId(automaticRule.conditionId)
+ || isValidScheduleConditionId(automaticRule.conditionId)) {
+ // set text if automatic rule end time is the latest active rule end time
+ long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
+ if (endTime > latestEndTime) {
+ latestEndTime = endTime;
+ secondaryText = automaticRule.name;
+ }
+ } else {
+ // set text if 3rd party rule
+ return automaticRule.name;
+ }
+ }
+ }
+
+ return !secondaryText.equals("") ? secondaryText : null;
+ }
+
+ private static long parseAutomaticRuleEndTime(Context context, Uri id) {
+ if (isValidEventConditionId(id)) {
+ // cannot look up end times for events
+ return Long.MAX_VALUE;
+ }
+
+ if (isValidScheduleConditionId(id)) {
+ ScheduleCalendar schedule = toScheduleCalendar(id);
+ long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
+
+ // check if automatic rule will end on next alarm
+ if (schedule.exitAtAlarm()) {
+ long nextAlarm = getNextAlarm(context);
+ schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
+ if (schedule.shouldExitForAlarm(endTimeMs)) {
+ return nextAlarm;
+ }
+ }
+
+ return endTimeMs;
+ }
+
+ return -1;
+ }
+
+ private static long getNextAlarm(Context context) {
+ final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
+ return info != null ? info.getTriggerTime() : 0;
+ }
}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index 11e1e67..e97f963a 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -40,6 +40,7 @@
*/
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_HAS_BUTTON,
+ FLAG_ICON_TINTABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flags {
@@ -49,6 +50,10 @@
* Flag for suggestion type with a single button
*/
public static final int FLAG_HAS_BUTTON = 1 << 0;
+ /**
+ * @hide
+ */
+ public static final int FLAG_ICON_TINTABLE = 1 << 1;
private final String mId;
private final CharSequence mTitle;
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index 0f85e1f..9f2182e 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -1,4 +1,3 @@
siyamed@google.com
nona@google.com
clarabayarri@google.com
-toki@google.com
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 413cd10..eba9129 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -166,7 +166,7 @@
* @return true if 24 hour time format is selected, false otherwise.
*/
public static boolean is24HourFormat(Context context) {
- return is24HourFormat(context, UserHandle.myUserId());
+ return is24HourFormat(context, context.getUserId());
}
/**
@@ -270,7 +270,7 @@
* @hide
*/
public static String getTimeFormatString(Context context) {
- return getTimeFormatString(context, UserHandle.myUserId());
+ return getTimeFormatString(context, context.getUserId());
}
/**
diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java
index 3524179..cd199b3 100644
--- a/core/java/android/text/style/DrawableMarginSpan.java
+++ b/core/java/android/text/style/DrawableMarginSpan.java
@@ -16,60 +16,100 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.Spanned;
-public class DrawableMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public DrawableMarginSpan(Drawable b) {
- mDrawable = b;
+/**
+ * A span which adds a drawable and a padding to the paragraph it's attached to.
+ * <p>
+ * If the height of the drawable is bigger than the height of the line it's attached to then the
+ * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a
+ * padding between the drawable and the text. The default value is 0. The span must be set from the
+ * beginning of the text, otherwise either the span won't be rendered or it will be rendered
+ * incorrectly.
+ * <p>
+ * For example, a drawable and a padding of 20px can be added like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with a drawable.");
+ * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" />
+ * <figcaption>Text with a drawable and a padding.</figcaption>
+ * <p>
+ *
+ * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of
+ * a {@link Drawable}.
+ */
+public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+ private static final int STANDARD_PAD_WIDTH = 0;
+
+ @NonNull
+ private final Drawable mDrawable;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0.
+ *
+ * @param drawable the drawable to be added
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable) {
+ this(drawable, STANDARD_PAD_WIDTH);
}
- public DrawableMarginSpan(Drawable b, int pad) {
- mDrawable = b;
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels.
+ *
+ * @param drawable the drawable to be added
+ * @param pad the distance between the drawable and the text
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable, int pad) {
+ mDrawable = drawable;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mDrawable.getIntrinsicWidth() + mPad;
}
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ @Override
+ public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+ int top, int baseline, int bottom,
+ @NonNull CharSequence text, int start, int end,
+ boolean first, @NonNull Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
- int ix = (int)x;
- int itop = (int)layout.getLineTop(layout.getLineForOffset(st));
+ int ix = (int) x;
+ int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
int dw = mDrawable.getIntrinsicWidth();
int dh = mDrawable.getIntrinsicHeight();
// XXX What to do about Paint?
- mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
+ mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
mDrawable.draw(c);
}
- public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ @Override
+ public void chooseHeight(@NonNull CharSequence text, int start, int end,
+ int istartv, int v,
+ @NonNull Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mDrawable.getIntrinsicHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
-
- private Drawable mDrawable;
- private int mPad;
}
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 5b8a6dd..1b16f33 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -16,6 +16,9 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -24,32 +27,71 @@
import java.lang.ref.WeakReference;
/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text.
+ * <p>
+ * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
+ * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
+ * <p>
+ * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
+ * looks like this:
+ * <pre>
+ * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
*
+ * private final Context mContext;
+ * private final int mResourceId;
+ *
+ * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
+ * mContext = context;
+ * mResourceId = resourceId;
+ * }
+ *
+ * {@literal @}Override
+ * public Drawable getDrawable() {
+ * Drawable drawable = mContext.getDrawable(mResourceId);
+ * drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ * return drawable;
+ * }
+ * }</pre>
+ * The class can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a drawable span");
+ * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
+ * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
+ * <figcaption>Replacing text with a drawable.</figcaption>
*/
public abstract class DynamicDrawableSpan extends ReplacementSpan {
- private static final String TAG = "DynamicDrawableSpan";
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the bottom of the surrounding text, i.e., at the same level as the
* lowest descender in the text.
*/
public static final int ALIGN_BOTTOM = 0;
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the baseline of the surrounding text.
*/
public static final int ALIGN_BASELINE = 1;
-
+
protected final int mVerticalAlignment;
-
+
+ private WeakReference<Drawable> mDrawableRef;
+
+ /**
+ * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
+ * {@link #ALIGN_BOTTOM}
+ */
public DynamicDrawableSpan() {
mVerticalAlignment = ALIGN_BOTTOM;
}
/**
- * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+ * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
+ *
+ * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
*/
protected DynamicDrawableSpan(int verticalAlignment) {
mVerticalAlignment = verticalAlignment;
@@ -64,22 +106,22 @@
}
/**
- * Your subclass must implement this method to provide the bitmap
+ * Your subclass must implement this method to provide the bitmap
* to be drawn. The dimensions of the bitmap must be the same
* from each call to the next.
*/
public abstract Drawable getDrawable();
@Override
- public int getSize(Paint paint, CharSequence text,
- int start, int end,
- Paint.FontMetricsInt fm) {
+ public int getSize(@NonNull Paint paint, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @Nullable Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
- fm.ascent = -rect.bottom;
- fm.descent = 0;
+ fm.ascent = -rect.bottom;
+ fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
@@ -89,12 +131,12 @@
}
@Override
- public void draw(Canvas canvas, CharSequence text,
- int start, int end, float x,
- int top, int y, int bottom, Paint paint) {
+ public void draw(@NonNull Canvas canvas, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+ int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
-
+
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
@@ -109,8 +151,9 @@
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;
- if (wr != null)
+ if (wr != null) {
d = wr.get();
+ }
if (d == null) {
d = getDrawable();
@@ -119,7 +162,5 @@
return d;
}
-
- private WeakReference<Drawable> mDrawableRef;
}
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index 304c83f..ad78bd5 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -16,57 +16,98 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.Spanned;
-public class IconMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public IconMarginSpan(Bitmap b) {
- mBitmap = b;
+/**
+ * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
+ * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
+ * span should be attached from the first character of the text.
+ * <p>
+ * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
+ * like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with icon and padding");
+ * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
+ * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
+ * <p>
+ *
+ * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
+ * a {@link Bitmap}.
+ */
+public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+
+ @NonNull
+ private final Bitmap mBitmap;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap) {
+ this(bitmap, 0);
}
- public IconMarginSpan(Bitmap b, int pad) {
- mBitmap = b;
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ * @param pad padding width, in pixels, between the bitmap and the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
+ mBitmap = bitmap;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mBitmap.getWidth() + mPad;
}
+ @Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ int top, int baseline, int bottom,
+ CharSequence text, int start, int end,
+ boolean first, Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
int itop = layout.getLineTop(layout.getLineForOffset(st));
- if (dir < 0)
+ if (dir < 0) {
x -= mBitmap.getWidth();
+ }
c.drawBitmap(mBitmap, x, itop, p);
}
+ @Override
public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ int istartv, int v,
+ Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mBitmap.getHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
- private Bitmap mBitmap;
- private int mPad;
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index b0bff68..95f0b43 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -17,6 +17,8 @@
package android.text.style;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -27,18 +29,49 @@
import java.io.InputStream;
+/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text. The drawable can be constructed from
+ * varied sources:
+ * <ul>
+ * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and
+ * {@link #ImageSpan(Context, Bitmap, int)}
+ * </li>
+ * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li>
+ * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li>
+ * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li>
+ * </ul>
+ * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ * <p>
+ * For example, an <code>ImagedSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = SpannableString("Bottom: span.\nBaseline: span.");
+ * // using the default alignment: ALIGN_BOTTOM
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE),
+ * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" />
+ * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption>
+ */
public class ImageSpan extends DynamicDrawableSpan {
+
+ @Nullable
private Drawable mDrawable;
+ @Nullable
private Uri mContentUri;
+ @DrawableRes
private int mResourceId;
+ @Nullable
private Context mContext;
+ @Nullable
private String mSource;
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b) {
+ public ImageSpan(@NonNull Bitmap b) {
this(null, b, ALIGN_BOTTOM);
}
@@ -46,80 +79,143 @@
* @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Bitmap b, int verticalAlignment) {
this(null, b, verticalAlignment);
}
- public ImageSpan(Context context, Bitmap b) {
- this(context, b, ALIGN_BOTTOM);
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param bitmap bitmap to be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) {
+ this(context, bitmap, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical
+ * alignment.
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display metrics of the resources
+ * @param bitmap bitmap to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mDrawable = context != null
- ? new BitmapDrawable(context.getResources(), b)
- : new BitmapDrawable(b);
+ ? new BitmapDrawable(context.getResources(), bitmap)
+ : new BitmapDrawable(bitmap);
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
- mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
- }
-
- public ImageSpan(Drawable d) {
- this(d, ALIGN_BOTTOM);
+ mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
}
/**
- * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * Constructs an {@link ImageSpan} from a drawable with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}.
+ *
+ * @param drawable drawable to be rendered
*/
- public ImageSpan(Drawable d, int verticalAlignment) {
- super(verticalAlignment);
- mDrawable = d;
- }
-
- public ImageSpan(Drawable d, String source) {
- this(d, source, ALIGN_BOTTOM);
+ public ImageSpan(@NonNull Drawable drawable) {
+ this(drawable, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a drawable and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Drawable d, String source, int verticalAlignment) {
+ public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
super(verticalAlignment);
- mDrawable = d;
+ mDrawable = drawable;
+ }
+
+ /**
+ * Constructs an {@link ImageSpan} from a drawable and a source with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's Uri source
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source) {
+ this(drawable, source, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's uri source
+ * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) {
+ super(verticalAlignment);
+ mDrawable = drawable;
mSource = source;
}
- public ImageSpan(Context context, Uri uri) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via
+ * {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri) {
this(context, uri, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical
+ * alignment. The Uri source can be retrieved via {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mContentUri = uri;
mSource = uri.toString();
}
- public ImageSpan(Context context, @DrawableRes int resourceId) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved
+ */
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) {
this(context, resourceId, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical
+ * alignment.
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId,
+ int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mResourceId = resourceId;
@@ -128,10 +224,10 @@
@Override
public Drawable getDrawable() {
Drawable drawable = null;
-
+
if (mDrawable != null) {
drawable = mDrawable;
- } else if (mContentUri != null) {
+ } else if (mContentUri != null) {
Bitmap bitmap = null;
try {
InputStream is = mContext.getContentResolver().openInputStream(
@@ -142,7 +238,7 @@
drawable.getIntrinsicHeight());
is.close();
} catch (Exception e) {
- Log.e("sms", "Failed to loaded content " + mContentUri, e);
+ Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e);
}
} else {
try {
@@ -150,8 +246,8 @@
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
} catch (Exception e) {
- Log.e("sms", "Unable to find resource: " + mResourceId);
- }
+ Log.e("ImageSpan", "Unable to find resource: " + mResourceId);
+ }
}
return drawable;
@@ -159,9 +255,12 @@
/**
* Returns the source string that was saved during construction.
+ *
+ * @return the source string that was saved during construction
+ * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
*/
+ @Nullable
public String getSource() {
return mSource;
}
-
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 1ebee82..50ee5f3 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,16 +19,42 @@
import android.graphics.Paint;
import android.text.TextPaint;
-public interface LineHeightSpan
-extends ParagraphStyle, WrapTogetherSpan
-{
+/**
+ * The classes that affect the height of the line should implement this interface.
+ */
+public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param fm font metrics of the paint, in integers
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm);
+ /**
+ * The classes that affect the height of the line with respect to density, should implement this
+ * interface.
+ */
public interface WithDensity extends LineHeightSpan {
+
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param paint the paint
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm, TextPaint paint);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm, TextPaint paint);
}
}
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 2ff52a8f..d76ef94 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -18,15 +18,36 @@
import android.graphics.MaskFilter;
import android.text.TextPaint;
-
+/**
+ * Span that allows setting a {@link MaskFilter} to the text it's attached to.
+ * <p>
+ * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used:
+ * <pre>
+ * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
+ * SpannableString string = new SpannableString("Text with blur mask");
+ * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
+ * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
+ */
public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
private MaskFilter mFilter;
+ /**
+ * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}.
+ *
+ * @param filter the filter to be applied to the <code>TextPaint</code>
+ */
public MaskFilterSpan(MaskFilter filter) {
mFilter = filter;
}
+ /**
+ * Return the mask filter for this span.
+ *
+ * @return the mask filter for this span
+ */
public MaskFilter getMaskFilter() {
return mFilter;
}
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index f900db5..bdfa700 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,55 +25,76 @@
import android.text.TextUtils;
/**
- *
- * Describes a style in a span.
+ * Span that allows setting the style of the text it's attached to.
+ * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
+ * {@link Typeface#BOLD_ITALIC}.
+ * <p>
* Note that styles are cumulative -- if both bold and italic are set in
* separate spans, or if the base style is bold and a span calls for italic,
* you get bold italic. You can't turn off a style from the base style.
- *
+ * <p>
+ * For example, the <code>StyleSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Bold and italic text");
+ * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
+ * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
*/
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
/**
- *
+ * Creates a {@link StyleSpan} from a style.
+ *
* @param style An integer constant describing the style for this span. Examples
- * include bold, italic, and normal. Values are constants defined
- * in {@link android.graphics.Typeface}.
+ * include bold, italic, and normal. Values are constants defined
+ * in {@link Typeface}.
*/
public StyleSpan(int style) {
mStyle = style;
}
- public StyleSpan(Parcel src) {
+ /**
+ * Creates a {@link StyleSpan} from a parcel.
+ *
+ * @param src the parcel
+ */
+ public StyleSpan(@NonNull Parcel src) {
mStyle = src.readInt();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.STYLE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mStyle);
}
/**
- * Returns the style constant defined in {@link android.graphics.Typeface}.
+ * Returns the style constant defined in {@link Typeface}.
*/
public int getStyle() {
return mStyle;
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 0566428..2cceb2c 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -16,39 +16,54 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.Px;
+
/**
- * Represents a single tab stop on a line.
+ * Paragraph affecting span that changes the position of the tab with respect to
+ * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
+ * encountered on the first line of the text.
*/
-public interface TabStopSpan
-extends ParagraphStyle
-{
- /**
- * Returns the offset of the tab stop from the leading margin of the
- * line.
- * @return the offset
- */
- public int getTabStop();
+public interface TabStopSpan extends ParagraphStyle {
/**
- * The default implementation of TabStopSpan.
+ * Returns the offset of the tab stop from the leading margin of the line, in pixels.
+ *
+ * @return the offset, in pixels
*/
- public static class Standard
- implements TabStopSpan
- {
+ int getTabStop();
+
+ /**
+ * The default implementation of TabStopSpan that allows setting the offset of the tab stop
+ * from the leading margin of the first line of text.
+ * <p>
+ * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i>
+ * and we want to move the tab stop with 100px. This can be achieved like this:
+ * <pre>
+ * SpannableString string = new SpannableString("\tParagraph text beginning with tab.");
+ * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" />
+ * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption>
+ */
+ class Standard implements TabStopSpan {
+
+ @Px
+ private int mTabOffset;
+
/**
- * Constructor.
+ * Constructs a {@link TabStopSpan.Standard} based on an offset.
*
- * @param where the offset of the tab stop from the leading margin of
- * the line
+ * @param offset the offset of the tab stop from the leading margin of
+ * the line, in pixels
*/
- public Standard(int where) {
- mTab = where;
+ public Standard(@IntRange(from = 0) int offset) {
+ mTabOffset = offset;
}
+ @Override
public int getTabStop() {
- return mTab;
+ return mTabOffset;
}
-
- private int mTab;
}
}
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index aa622d8..1622812 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,42 +25,59 @@
import android.text.TextUtils;
/**
- * Changes the typeface family of the text to which the span is attached.
+ * Changes the typeface family of the text to which the span is attached. Examples of typeface
+ * family include "monospace", "serif", and "sans-serif".
+ * <p>
+ * For example, change the typeface of a text to "monospace" like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with typeface span");
+ * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
+ * <figcaption>Text with "monospace" typeface family.</figcaption>
*/
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+
private final String mFamily;
/**
- * @param family The font family for this typeface. Examples include
- * "monospace", "serif", and "sans-serif".
+ * Constructs a {@link TypefaceSpan} based on a font family.
+ *
+ * @param family The font family for this typeface. Examples include
+ * "monospace", "serif", and "sans-serif".
*/
public TypefaceSpan(String family) {
mFamily = family;
}
- public TypefaceSpan(Parcel src) {
+ public TypefaceSpan(@NonNull Parcel src) {
mFamily = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.TYPEFACE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mFamily);
}
@@ -71,16 +89,16 @@
}
@Override
- public void updateDrawState(TextPaint ds) {
- apply(ds, mFamily);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
@Override
- public void updateMeasureState(TextPaint paint) {
- apply(paint, mFamily);
+ public void updateMeasureState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
- private static void apply(Paint paint, String family) {
+ private static void apply(@NonNull Paint paint, String family) {
int oldStyle;
Typeface old = paint.getTypeface();
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 58239ef..eab1ef4 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -27,40 +28,71 @@
import android.util.Log;
import android.view.View;
+/**
+ * Implementation of the {@link ClickableSpan} that allows setting a url string. When
+ * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code>
+ * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.
+ * <p>
+ * For example, a <code>URLSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a url span");
+ * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
+ * <figcaption>Text with <code>URLSpan</code>.</figcaption>
+ */
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
+ /**
+ * Constructs a {@link URLSpan} from a url string.
+ *
+ * @param url the url string
+ */
public URLSpan(String url) {
mURL = url;
}
- public URLSpan(Parcel src) {
+ /**
+ * Constructs a {@link URLSpan} from a parcel.
+ */
+ public URLSpan(@NonNull Parcel src) {
mURL = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.URL_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mURL);
}
+ /**
+ * Get the url string for this span.
+ *
+ * @return the url string.
+ */
public String getURL() {
return mURL;
}
diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java
index da14834..172c837 100644
--- a/core/java/android/transition/ArcMotion.java
+++ b/core/java/android/transition/ArcMotion.java
@@ -216,7 +216,13 @@
boolean isMovingUpwards = startY > endY;
- if ((Math.abs(deltaX) < Math.abs(deltaY))) {
+ if (deltaY == 0) {
+ ex = dx;
+ ey = dy + (Math.abs(deltaX) * 0.5f * mMinimumHorizontalTangent);
+ } else if (deltaX == 0) {
+ ex = dx + (Math.abs(deltaY) * 0.5f * mMinimumVerticalTangent);
+ ey = dy;
+ } else if ((Math.abs(deltaX) < Math.abs(deltaY))) {
// Similar triangles bfa and bde mean that (ab/fb = eb/bd)
// Therefore, eb = ab * bd / fb
// ab = hypotenuse
@@ -254,7 +260,7 @@
float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
float newArcDistance2 = 0;
- if (arcDist2 < minimumArcDist2) {
+ if (arcDist2 != 0 && arcDist2 < minimumArcDist2) {
newArcDistance2 = minimumArcDist2;
} else if (arcDist2 > maximumArcDist2) {
newArcDistance2 = maximumArcDist2;
diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java
index 084b79d..60b77bc 100644
--- a/core/java/android/transition/TransitionUtils.java
+++ b/core/java/android/transition/TransitionUtils.java
@@ -163,10 +163,14 @@
public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds,
ViewGroup sceneRoot) {
final boolean addToOverlay = !view.isAttachedToWindow();
+ ViewGroup parent = null;
+ int indexInParent = 0;
if (addToOverlay) {
if (sceneRoot == null || !sceneRoot.isAttachedToWindow()) {
return null;
}
+ parent = (ViewGroup) view.getParent();
+ indexInParent = parent.indexOfChild(view);
sceneRoot.getOverlay().add(view);
}
Bitmap bitmap = null;
@@ -190,6 +194,7 @@
}
if (addToOverlay) {
sceneRoot.getOverlay().remove(view);
+ parent.addView(view, indexInParent);
}
return bitmap;
}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index f0838a1..77c652e 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -402,8 +402,11 @@
// Becoming GONE
if (startView == endView) {
viewToKeep = endView;
- } else {
+ } else if (mCanRemoveViews) {
overlayView = startView;
+ } else {
+ overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
+ (View) startView.getParent());
}
}
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index b7099b6..13de172 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -120,6 +120,14 @@
public static final int DENSITY_420 = 420;
/**
+ * Intermediate density for screens that sit somewhere between
+ * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+ * This is not a density that applications should target, instead relying
+ * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+ */
+ public static final int DENSITY_440 = 440;
+
+ /**
* Standard quantized DPI for extra-extra-high-density screens.
*/
public static final int DENSITY_XXHIGH = 480;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 410cdc6..1ead0b4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,13 +37,13 @@
private static final Map<String, String> DEFAULT_FLAGS;
static {
DEFAULT_FLAGS = new HashMap<>();
- DEFAULT_FLAGS.put("device_info_v2", "true");
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", "false");
+ DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
+ DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
}
/**
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
deleted file mode 100644
index 51fb18a..0000000
--- a/core/java/android/util/StatsManager.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright 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.util;
-
-import android.Manifest;
-import android.annotation.RequiresPermission;
-import android.os.IBinder;
-import android.os.IStatsManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-
-/*
- *
- *
- *
- *
- * THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS.
- * The new StatsManager is to be found in android.app.StatsManager.
- * TODO: Delete this file!
- *
- *
- *
- *
- */
-
-
-/**
- * API for StatsD clients to send configurations and retrieve data.
- *
- * @hide
- */
-public class StatsManager {
- IStatsManager mService;
- private static final String TAG = "StatsManager";
-
- /**
- * Constructor for StatsManagerClient.
- *
- * @hide
- */
- public StatsManager() {
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
- return false;
- }
- }
- }
-
- /**
- * Clients can send a configuration and simultaneously registers the name of a broadcast
- * receiver that listens for when it should request data.
- *
- * @param configKey An arbitrary integer that allows clients to track the configuration.
- * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
- * dependencies eg, conditions and matchers).
- * @param pkg The package name to receive the broadcast.
- * @param cls The name of the class that receives the broadcast.
- * @return true if successful
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(configKey, config, pkg, cls);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
- return false;
- }
- }
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(String configKey) {
- // To prevent breakages of old dependencies.
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(Long.parseLong(configKey));
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
- return false;
- }
- }
- }
-
- /**
- * Remove a configuration from logging.
- *
- * @param configKey Configuration key to remove.
- * @return true if successful
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(long configKey) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(configKey);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
- return false;
- }
- }
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(String configKey) {
- // TODO: remove this and all other methods with String-based config keys.
- // To prevent build breakages of dependencies.
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
- return null;
- }
- return service.getData(Long.parseLong(configKey));
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
- return null;
- }
- }
- }
-
- /**
- * Clients can request data with a binder call. This getter is destructive and also clears
- * the retrieved metrics from statsd memory.
- *
- * @param configKey Configuration key to retrieve data from.
- * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(long configKey) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
- return null;
- }
- return service.getData(configKey);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
- return null;
- }
- }
- }
-
- /**
- * Clients can request metadata for statsd. Will contain stats across all configurations but not
- * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
- * This getter is not destructive and will not reset any metrics/counters.
- *
- * @return Serialized StatsdStatsReport proto. Returns null on failure.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getMetadata() {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting metadata");
- return null;
- }
- return service.getMetadata();
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting metadata");
- return null;
- }
- }
- }
-
- private class StatsdDeathRecipient implements IBinder.DeathRecipient {
- @Override
- public void binderDied() {
- synchronized (this) {
- mService = null;
- }
- }
- }
-
- private IStatsManager getIStatsManagerLocked() throws RemoteException {
- if (mService != null) {
- return mService;
- }
- mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
- if (mService != null) {
- mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
- }
- return mService;
- }
-}
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 4c6e511..a3eeb27 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -106,18 +106,22 @@
calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
- md.update(DEFAULT_SALT);
- md.update(verityBlock);
+ md.update(header);
+ md.update(extensions);
md.update(apkDigest);
return md.digest();
}
+ /**
+ * Internal method to generate various parts of FSVerity constructs, including the header,
+ * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the
+ * generated data size and is readey for consuming.
+ */
private static void calculateFsveritySignatureInternal(
RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
throws IOException, NoSuchAlgorithmException, DigestException {
assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-
long signingBlockSize =
signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
long dataSize = apk.length() - signingBlockSize - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
@@ -128,6 +132,7 @@
levelOffset, treeOutput);
if (rootHashOutput != null) {
rootHashOutput.put(apkRootHash);
+ rootHashOutput.flip();
}
}
@@ -333,9 +338,9 @@
buffer.put((byte) 0); // auth block offset, disabled here
buffer.put((byte) 2); // extension count
buffer.put(salt); // salt (8 bytes)
- // skip(buffer, 22); // reserved
+ skip(buffer, 22); // reserved
- buffer.rewind();
+ buffer.flip();
return buffer;
}
@@ -396,12 +401,10 @@
buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length
skip(buffer, 7); // reserved
buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes
-
- // There are extra kPadding bytes of 0s here, included in the total size field of the
- // extension header. The output ByteBuffer is assumed to be initialized to 0.
+ skip(buffer, kPadding); // padding
}
- buffer.rewind();
+ buffer.flip();
return buffer;
}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index ea6226b..69973e6 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
import android.view.IRecentsAnimationController;
@@ -28,15 +29,26 @@
oneway interface IRecentsAnimationRunner {
/**
- * Called when the system is ready for the handler to start animating all the visible tasks.
+ * Deprecated, to be removed once Launcher updates
*/
void onAnimationStart(in IRecentsAnimationController controller,
- in RemoteAnimationTarget[] apps);
+ in RemoteAnimationTarget[] apps) = 0;
/**
* Called when the system needs to cancel the current animation. This can be due to the
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time.
*/
- void onAnimationCanceled();
+ void onAnimationCanceled() = 1;
+
+ /**
+ * Called when the system is ready for the handler to start animating all the visible tasks.
+ *
+ * @param homeContentInsets The current home app content insets
+ * @param minimizedHomeBounds Specifies the bounds of the minimized home app, will be
+ * {@code null} if the device is not currently in split screen
+ */
+ void onAnimationStart_New(in IRecentsAnimationController controller,
+ in RemoteAnimationTarget[] apps, in Rect homeContentInsets,
+ in Rect minimizedHomeBounds) = 2;
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 3fd4696..8cb46b7 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -23,6 +23,10 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -396,6 +400,33 @@
return true;
}
+ /**
+ * Get the Bitmap from the Drawable.
+ *
+ * If the Bitmap needed to be scaled up to account for density, BitmapDrawable
+ * handles this at draw time. But this class doesn't actually draw the Bitmap;
+ * it is just a holder for native code to access its SkBitmap. So this needs to
+ * get a version that is scaled to account for density.
+ */
+ private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
+ Bitmap bitmap = bitmapDrawable.getBitmap();
+ final int scaledWidth = bitmapDrawable.getIntrinsicWidth();
+ final int scaledHeight = bitmapDrawable.getIntrinsicHeight();
+ if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) {
+ return bitmap;
+ }
+
+ Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ RectF dst = new RectF(0, 0, scaledWidth, scaledHeight);
+
+ Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig());
+ Canvas canvas = new Canvas(scaled);
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(bitmap, src, dst, paint);
+ return scaled;
+ }
+
private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
final XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
@@ -452,7 +483,8 @@
+ "is different. All frames should have the exact same size and "
+ "share the same hotspot.");
}
- mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
+ BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
+ mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
}
}
}
@@ -461,7 +493,8 @@
+ "refer to a bitmap drawable.");
}
- final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
validateHotSpot(bitmap, hotSpotX, hotSpotY);
// Set the properties now that we have successfully loaded the icon.
mBitmap = bitmap;
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index c28c389..facf575 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -79,6 +79,11 @@
public final Rect clipRect;
/**
+ * The insets of the main app window.
+ */
+ public final Rect contentInsets;
+
+ /**
* The index of the element in the tree in prefix order. This should be used for z-layering
* to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
* happen.
@@ -105,13 +110,14 @@
public final WindowConfiguration windowConfiguration;
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
- Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
- WindowConfiguration windowConfig) {
+ Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
+ Rect sourceContainerBounds, WindowConfiguration windowConfig) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
this.isTranslucent = isTranslucent;
this.clipRect = new Rect(clipRect);
+ this.contentInsets = new Rect(contentInsets);
this.prefixOrderIndex = prefixOrderIndex;
this.position = new Point(position);
this.sourceContainerBounds = new Rect(sourceContainerBounds);
@@ -124,6 +130,7 @@
leash = in.readParcelable(null);
isTranslucent = in.readBoolean();
clipRect = in.readParcelable(null);
+ contentInsets = in.readParcelable(null);
prefixOrderIndex = in.readInt();
position = in.readParcelable(null);
sourceContainerBounds = in.readParcelable(null);
@@ -142,6 +149,7 @@
dest.writeParcelable(leash, 0 /* flags */);
dest.writeBoolean(isTranslucent);
dest.writeParcelable(clipRect, 0 /* flags */);
+ dest.writeParcelable(contentInsets, 0 /* flags */);
dest.writeInt(prefixOrderIndex);
dest.writeParcelable(position, 0 /* flags */);
dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3cbd275..5de25ba 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -74,6 +75,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
+import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -905,6 +907,13 @@
*/
private static boolean sThrowOnInvalidFloatProperties;
+ /**
+ * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow.
+ * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface
+ * instead.
+ */
+ private static boolean sAcceptZeroSizeDragShadow;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -4797,6 +4806,7 @@
Canvas.sCompatibilityRestore = targetSdkVersion < Build.VERSION_CODES.M;
Canvas.sCompatibilitySetBitmap = targetSdkVersion < Build.VERSION_CODES.O;
+ Canvas.setCompatibilityVersion(targetSdkVersion);
// In M and newer, our widgets can pass a "hint" value in the size
// for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers
@@ -4839,6 +4849,8 @@
sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+ sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -7940,12 +7952,22 @@
* optimal implementation providing this data.
*/
public void onProvideVirtualStructure(ViewStructure structure) {
- AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+ onProvideVirtualStructureCompat(structure, false);
+ }
+
+ /**
+ * Fallback implementation to populate a ViewStructure from accessibility state.
+ *
+ * @param structure The structure to populate.
+ * @param forAutofill Whether the structure is needed for autofill.
+ */
+ private void onProvideVirtualStructureCompat(ViewStructure structure, boolean forAutofill) {
+ final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
if (provider != null) {
- AccessibilityNodeInfo info = createAccessibilityNodeInfo();
+ final AccessibilityNodeInfo info = createAccessibilityNodeInfo();
structure.setChildCount(1);
- ViewStructure root = structure.newChild(0);
- populateVirtualStructure(root, provider, info);
+ final ViewStructure root = structure.newChild(0);
+ populateVirtualStructure(root, provider, info, forAutofill);
info.recycle();
}
}
@@ -7974,6 +7996,9 @@
* <li>Call {@link android.view.autofill.AutofillManager#notifyViewEntered(View, int, Rect)}
* and/or {@link android.view.autofill.AutofillManager#notifyViewExited(View, int)}
* when the focused virtual child changed.
+ * <li>Override {@link #isVisibleToUserForAutofill(int)} to allow the platform to query
+ * whether a given virtual view is visible to the user in order to support triggering
+ * save when all views of interest go away.
* <li>Call
* {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)}
* when the value of a virtual child changed.
@@ -8009,6 +8034,9 @@
* @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
+ if (mContext.isAutofillCompatibilityEnabled()) {
+ onProvideVirtualStructureCompat(structure, true);
+ }
}
/**
@@ -8086,6 +8114,25 @@
* @attr ref android.R.styleable#Theme_autofilledHighlight
*/
public void autofill(@NonNull @SuppressWarnings("unused") SparseArray<AutofillValue> values) {
+ if (!mContext.isAutofillCompatibilityEnabled()) {
+ return;
+ }
+ final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+ if (provider == null) {
+ return;
+ }
+ final int valueCount = values.size();
+ for (int i = 0; i < valueCount; i++) {
+ final AutofillValue value = values.valueAt(i);
+ if (value.isText()) {
+ final int virtualId = values.keyAt(i);
+ final CharSequence text = value.getTextValue();
+ final Bundle arguments = new Bundle();
+ arguments.putCharSequence(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
+ provider.performAction(virtualId, AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
+ }
+ }
}
/**
@@ -8339,9 +8386,10 @@
}
private void populateVirtualStructure(ViewStructure structure,
- AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
+ AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
+ boolean forAutofill) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
- null, null, null);
+ null, null, info.getViewIdResourceName());
Rect rect = structure.getTempRect();
info.getBoundsInParent(rect);
structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
@@ -8374,21 +8422,51 @@
if (info.isContextClickable()) {
structure.setContextClickable(true);
}
+ if (forAutofill) {
+ structure.setAutofillId(new AutofillId(getAutofillId(),
+ AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
+ }
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
+ if (forAutofill) {
+ final int maxTextLength = info.getMaxTextLength();
+ if (maxTextLength != -1) {
+ structure.setMaxTextLength(maxTextLength);
+ }
+ structure.setHint(info.getHintText());
+ }
if ((info.getText() != null || info.getError() != null)) {
structure.setText(info.getText(), info.getTextSelectionStart(),
info.getTextSelectionEnd());
+ if (forAutofill) {
+ if (info.isEditable()) {
+ structure.setDataIsSensitive(true);
+ structure.setAutofillType(AUTOFILL_TYPE_TEXT);
+ final AutofillValue autofillValue = AutofillValue.forText(structure.getText());
+ structure.setAutofillValue(autofillValue);
+ if (info.isPassword()) {
+ structure.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ }
+ } else {
+ structure.setDataIsSensitive(false);
+ }
+ }
}
final int NCHILDREN = info.getChildCount();
if (NCHILDREN > 0) {
structure.setChildCount(NCHILDREN);
for (int i=0; i<NCHILDREN; i++) {
+ if (AccessibilityNodeInfo.getVirtualDescendantId(info.getChildNodeIds().get(i))
+ == AccessibilityNodeProvider.HOST_VIEW_ID) {
+ Log.e(VIEW_LOG_TAG, "Virtual view pointing to its host. Ignoring");
+ continue;
+ }
AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo(
AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
ViewStructure child = structure.newChild(i);
- populateVirtualStructure(child, provider, cinfo);
+ populateVirtualStructure(child, provider, cinfo, forAutofill);
cinfo.recycle();
}
}
@@ -8717,6 +8795,24 @@
}
/**
+ * Computes whether this virtual autofill view is visible to the user.
+ *
+ * @return Whether the view is visible on the screen.
+ */
+ public boolean isVisibleToUserForAutofill(int virtualId) {
+ if (mContext.isAutofillCompatibilityEnabled()) {
+ final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+ if (provider != null) {
+ final AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(virtualId);
+ if (node != null) {
+ return node.isVisibleToUser();
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Computes whether this view is visible to the user. Such a view is
* attached, visible, all its predecessors are visible, it is not clipped
* entirely by its predecessors, and has an alpha greater than zero.
@@ -8725,7 +8821,7 @@
*
* @hide
*/
- protected boolean isVisibleToUser() {
+ public boolean isVisibleToUser() {
return isVisibleToUser(null);
}
@@ -23542,8 +23638,7 @@
* constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)}
* and {@link #onDrawShadow(Canvas)} methods are also overridden in order
* to supply the drag shadow's dimensions and appearance without
- * reference to any View object. If they are not overridden, then the result is an
- * invisible drag shadow.
+ * reference to any View object.
*/
public DragShadowBuilder() {
mView = new WeakReference<View>(null);
@@ -23689,9 +23784,19 @@
Point shadowTouchPoint = new Point();
shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
- if ((shadowSize.x <= 0) || (shadowSize.y <= 0)
+ if ((shadowSize.x < 0) || (shadowSize.y < 0)
|| (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
- throw new IllegalStateException("Drag shadow dimensions must be positive");
+ throw new IllegalStateException("Drag shadow dimensions must not be negative");
+ }
+
+ // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
+ // does not accept zero size surface.
+ if (shadowSize.x == 0 || shadowSize.y == 0) {
+ if (!sAcceptZeroSizeDragShadow) {
+ throw new IllegalStateException("Drag shadow dimensions must be positive");
+ }
+ shadowSize.x = 1;
+ shadowSize.y = 1;
}
if (ViewDebug.DEBUG_DRAG) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a8bdb85..8e60a72 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3555,13 +3555,16 @@
public void dispatchProvideAutofillStructure(ViewStructure structure,
@AutofillFlags int flags) {
super.dispatchProvideAutofillStructure(structure, flags);
+
if (structure.getChildCount() != 0) {
return;
}
if (!isLaidOut()) {
- Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
- + mChildrenCount + " children of " + getAutofillId());
+ if (Helper.sVerbose) {
+ Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
+ + mChildrenCount + " children of " + getAutofillId());
+ }
return;
}
@@ -3968,15 +3971,7 @@
}
/**
- * Layout debugging code which draws rectangles around layout params.
- *
- * <p>This function is called automatically when the developer setting is enabled.<p/>
- *
- * <p>It is strongly advised to only call this function from debug builds as there is
- * a risk of leaking unwanted layout information.<p/>
- *
- * @param canvas the canvas on which to draw
- * @param paint the paint used to draw through
+ * @hide
*/
protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
for (int i = 0; i < getChildCount(); i++) {
@@ -3986,19 +3981,7 @@
}
/**
- * Layout debugging code which draws rectangles around:
- * <ul>
- * <li>optical bounds<li/>
- * <li>margins<li/>
- * <li>clip bounds<li/>
- * <ul/>
- *
- * <p>This function is called automatically when the developer setting is enabled.<p/>
- *
- * <p>It is strongly advised to only call this function from debug builds as there is
- * a risk of leaking unwanted layout information.<p/>
- *
- * @param canvas the canvas on which to draw
+ * @hide
*/
protected void onDebugDraw(Canvas canvas) {
Paint paint = getDebugPaint();
@@ -5120,6 +5103,20 @@
// manually assembling the hierarchy, update the ancestor default-focus chain.
setDefaultFocus(child);
}
+
+ touchAccessibilityNodeProviderIfNeeded(child);
+ }
+
+ /**
+ * We may need to touch the provider to bring up the a11y layer. In a11y mode
+ * clients inspect the screen or the user touches it which triggers bringing up
+ * of the a11y infrastructure while in autofill mode we want the infra up and
+ * running from the beginning since we watch for a11y events to drive autofill.
+ */
+ private void touchAccessibilityNodeProviderIfNeeded(View child) {
+ if (mContext.isAutofillCompatibilityEnabled()) {
+ child.getAccessibilityNodeProvider();
+ }
}
private void addInArray(View child, int index) {
@@ -7715,14 +7712,10 @@
/**
* Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
*
- * <p>This function is called automatically when the developer setting is enabled.<p/>
- *
- * <p>It is strongly advised to only call this function from debug builds as there is
- * a risk of leaking unwanted layout information.<p/>
- *
* @param view the view that contains these layout parameters
* @param canvas the canvas on which to draw
- * @param paint the paint used to draw through
+ *
+ * @hide
*/
public void onDebugDraw(View view, Canvas canvas, Paint paint) {
}
@@ -8226,6 +8219,9 @@
return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
}
+ /**
+ * @hide
+ */
@Override
public void onDebugDraw(View view, Canvas canvas, Paint paint) {
Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7bd197e..810864e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -71,6 +71,7 @@
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.LongArray;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
@@ -114,6 +115,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
import java.util.concurrent.CountDownLatch;
/**
@@ -2569,6 +2572,10 @@
~WindowManager.LayoutParams
.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
+
+ // Refocusing a window that has a focused view should fire a
+ // focus event for the view since the global focused view changed.
+ fireAccessibilityFocusEventIfHasFocusedNode();
} else {
if (mPointerCapture) {
handlePointerCaptureChanged(false);
@@ -2578,6 +2585,86 @@
mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
}
+ private void fireAccessibilityFocusEventIfHasFocusedNode() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return;
+ }
+ final View focusedView = mView.findFocus();
+ if (focusedView == null) {
+ return;
+ }
+ final AccessibilityNodeProvider provider = focusedView.getAccessibilityNodeProvider();
+ if (provider == null) {
+ focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ } else {
+ final AccessibilityNodeInfo focusedNode = findFocusedVirtualNode(provider);
+ if (focusedNode != null) {
+ final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
+ focusedNode.getSourceNodeId());
+ // This is a best effort since clearing and setting the focus via the
+ // provider APIs could have side effects. We don't have a provider API
+ // similar to that on View to ask a given event to be fired.
+ final AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ event.setSource(focusedView, virtualId);
+ event.setPackageName(focusedNode.getPackageName());
+ event.setChecked(focusedNode.isChecked());
+ event.setContentDescription(focusedNode.getContentDescription());
+ event.setPassword(focusedNode.isPassword());
+ event.getText().add(focusedNode.getText());
+ event.setEnabled(focusedNode.isEnabled());
+ focusedView.getParent().requestSendAccessibilityEvent(focusedView, event);
+ focusedNode.recycle();
+ }
+ }
+ }
+
+ private AccessibilityNodeInfo findFocusedVirtualNode(AccessibilityNodeProvider provider) {
+ AccessibilityNodeInfo focusedNode = provider.findFocus(
+ AccessibilityNodeInfo.FOCUS_INPUT);
+ if (focusedNode != null) {
+ return focusedNode;
+ }
+
+ if (!mContext.isAutofillCompatibilityEnabled()) {
+ return null;
+ }
+
+ // Unfortunately some provider implementations don't properly
+ // implement AccessibilityNodeProvider#findFocus
+ AccessibilityNodeInfo current = provider.createAccessibilityNodeInfo(
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+ if (current.isFocused()) {
+ return current;
+ }
+
+ final Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
+ fringe.offer(current);
+
+ while (!fringe.isEmpty()) {
+ current = fringe.poll();
+ final LongArray childNodeIds = current.getChildNodeIds();
+ if (childNodeIds== null || childNodeIds.size() <= 0) {
+ continue;
+ }
+ final int childCount = childNodeIds.size();
+ for (int i = 0; i < childCount; i++) {
+ final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
+ childNodeIds.get(i));
+ final AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(virtualId);
+ if (child != null) {
+ if (child.isFocused()) {
+ return child;
+ }
+ fringe.offer(child);
+ }
+ }
+ current.recycle();
+ }
+
+ return null;
+ }
+
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index b4499d1..191c270 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -16,10 +16,9 @@
package android.view.accessibility;
-import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
-
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -44,7 +43,7 @@
import android.util.SparseArray;
import android.view.IWindow;
import android.view.View;
-
+import android.view.accessibility.AccessibilityEvent.EventType;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;
@@ -52,6 +51,8 @@
import java.util.Collections;
import java.util.List;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+
/**
* System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
* and provides facilities for querying the accessibility state of the system.
@@ -132,6 +133,8 @@
boolean mIsHighTextContrastEnabled;
+ AccessibilityPolicy mAccessibilityPolicy;
+
private final ArrayMap<AccessibilityStateChangeListener, Handler>
mAccessibilityStateChangeListeners = new ArrayMap<>();
@@ -215,6 +218,60 @@
void onHighTextContrastStateChanged(boolean enabled);
}
+ /**
+ * Policy to inject behavior into the accessibility manager.
+ *
+ * @hide
+ */
+ public interface AccessibilityPolicy {
+ /**
+ * Checks whether accessibility is enabled.
+ *
+ * @param accessibilityEnabled Whether the accessibility layer is enabled.
+ * @return whether accessibility is enabled.
+ */
+ boolean isEnabled(boolean accessibilityEnabled);
+
+ /**
+ * Notifies the policy for an accessibility event.
+ *
+ * @param event The event.
+ * @param accessibilityEnabled Whether the accessibility layer is enabled.
+ * @param relevantEventTypes The events relevant events.
+ * @return The event to dispatch or null.
+ */
+ @Nullable AccessibilityEvent onAccessibilityEvent(@NonNull AccessibilityEvent event,
+ boolean accessibilityEnabled, @EventType int relevantEventTypes);
+
+ /**
+ * Gets the list of relevant events.
+ *
+ * @param relevantEventTypes The relevant events.
+ * @return The relevant events to report.
+ */
+ @EventType int getRelevantEventTypes(@EventType int relevantEventTypes);
+
+ /**
+ * Gets the list of installed services to report.
+ *
+ * @param installedService The installed services.
+ * @return The services to report.
+ */
+ @NonNull List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+ @Nullable List<AccessibilityServiceInfo> installedService);
+
+ /**
+ * Gets the list of enabled accessibility services.
+ *
+ * @param feedbackTypeFlags The feedback type to query for.
+ * @param enabledService The enabled services.
+ * @return The services to report.
+ */
+ @Nullable List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+ @FeedbackType int feedbackTypeFlags,
+ @Nullable List<AccessibilityServiceInfo> enabledService);
+ }
+
private final IAccessibilityManagerClient.Stub mClient =
new IAccessibilityManagerClient.Stub() {
@Override
@@ -273,7 +330,7 @@
== PackageManager.PERMISSION_GRANTED) {
userId = UserHandle.USER_CURRENT;
} else {
- userId = UserHandle.myUserId();
+ userId = context.getUserId();
}
sInstance = new AccessibilityManager(context, null, userId);
}
@@ -341,11 +398,8 @@
*/
public boolean isEnabled() {
synchronized (mLock) {
- IAccessibilityManager service = getServiceLocked();
- if (service == null) {
- return false;
- }
- return mIsEnabled;
+ return mIsEnabled || (mAccessibilityPolicy != null
+ && mAccessibilityPolicy.isEnabled(mIsEnabled));
}
}
@@ -401,12 +455,23 @@
public void sendAccessibilityEvent(AccessibilityEvent event) {
final IAccessibilityManager service;
final int userId;
+ final AccessibilityEvent dispatchedEvent;
synchronized (mLock) {
service = getServiceLocked();
if (service == null) {
return;
}
- if (!mIsEnabled) {
+ event.setEventTime(SystemClock.uptimeMillis());
+ if (mAccessibilityPolicy != null) {
+ dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event,
+ mIsEnabled, mRelevantEventTypes);
+ if (dispatchedEvent == null) {
+ return;
+ }
+ } else {
+ dispatchedEvent = event;
+ }
+ if (!isEnabled()) {
Looper myLooper = Looper.myLooper();
if (myLooper == Looper.getMainLooper()) {
throw new IllegalStateException(
@@ -420,9 +485,9 @@
return;
}
}
- if ((event.getEventType() & mRelevantEventTypes) == 0) {
+ if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) {
if (DEBUG) {
- Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+ Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent
+ " that is not among "
+ AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
}
@@ -431,23 +496,25 @@
userId = mUserId;
}
try {
- event.setEventTime(SystemClock.uptimeMillis());
// it is possible that this manager is in the same process as the service but
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
try {
- service.sendAccessibilityEvent(event, userId);
+ service.sendAccessibilityEvent(dispatchedEvent, userId);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
if (DEBUG) {
- Log.i(LOG_TAG, event + " sent");
+ Log.i(LOG_TAG, dispatchedEvent + " sent");
}
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+ Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
} finally {
- event.recycle();
+ if (event != dispatchedEvent) {
+ event.recycle();
+ }
+ dispatchedEvent.recycle();
}
}
@@ -462,7 +529,7 @@
if (service == null) {
return;
}
- if (!mIsEnabled) {
+ if (!isEnabled()) {
Looper myLooper = Looper.myLooper();
if (myLooper == Looper.getMainLooper()) {
throw new IllegalStateException(
@@ -532,6 +599,9 @@
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
}
+ if (mAccessibilityPolicy != null) {
+ services = mAccessibilityPolicy.getInstalledAccessibilityServiceList(services);
+ }
if (services != null) {
return Collections.unmodifiableList(services);
} else {
@@ -574,6 +644,10 @@
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
}
+ if (mAccessibilityPolicy != null) {
+ services = mAccessibilityPolicy.getEnabledAccessibilityServiceList(
+ feedbackTypeFlags, services);
+ }
if (services != null) {
return Collections.unmodifiableList(services);
} else {
@@ -783,6 +857,19 @@
}
/**
+ * Sets the {@link AccessibilityPolicy} controlling this manager.
+ *
+ * @param policy The policy.
+ *
+ * @hide
+ */
+ public void setAccessibilityPolicy(@Nullable AccessibilityPolicy policy) {
+ synchronized (mLock) {
+ mAccessibilityPolicy = policy;
+ }
+ }
+
+ /**
* Check if the accessibility volume stream is active.
*
* @return True if accessibility volume is active (i.e. some service has requested it). False
@@ -834,7 +921,7 @@
final boolean highTextContrastEnabled =
(stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
- final boolean wasEnabled = mIsEnabled;
+ final boolean wasEnabled = isEnabled();
final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
@@ -843,7 +930,7 @@
mIsTouchExplorationEnabled = touchExplorationEnabled;
mIsHighTextContrastEnabled = highTextContrastEnabled;
- if (wasEnabled != enabled) {
+ if (wasEnabled != isEnabled()) {
notifyAccessibilityStateChanged();
}
@@ -1052,16 +1139,15 @@
if (mAccessibilityStateChangeListeners.isEmpty()) {
return;
}
- isEnabled = mIsEnabled;
+ isEnabled = isEnabled();
listeners = new ArrayMap<>(mAccessibilityStateChangeListeners);
}
- int numListeners = listeners.size();
+ final int numListeners = listeners.size();
for (int i = 0; i < numListeners; i++) {
- final AccessibilityStateChangeListener listener =
- mAccessibilityStateChangeListeners.keyAt(i);
- mAccessibilityStateChangeListeners.valueAt(i)
- .post(() -> listener.onAccessibilityStateChanged(isEnabled));
+ final AccessibilityStateChangeListener listener = listeners.keyAt(i);
+ listeners.valueAt(i).post(() ->
+ listener.onAccessibilityStateChanged(isEnabled));
}
}
@@ -1079,12 +1165,11 @@
listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners);
}
- int numListeners = listeners.size();
+ final int numListeners = listeners.size();
for (int i = 0; i < numListeners; i++) {
- final TouchExplorationStateChangeListener listener =
- mTouchExplorationStateChangeListeners.keyAt(i);
- mTouchExplorationStateChangeListeners.valueAt(i)
- .post(() -> listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
+ final TouchExplorationStateChangeListener listener = listeners.keyAt(i);
+ listeners.valueAt(i).post(() ->
+ listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
}
}
@@ -1102,12 +1187,11 @@
listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
}
- int numListeners = listeners.size();
+ final int numListeners = listeners.size();
for (int i = 0; i < numListeners; i++) {
- final HighTextContrastChangeListener listener =
- mHighTextContrastStateChangeListeners.keyAt(i);
- mHighTextContrastStateChangeListeners.valueAt(i)
- .post(() -> listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
+ final HighTextContrastChangeListener listener = listeners.keyAt(i);
+ listeners.valueAt(i).post(() ->
+ listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 23e7d61..417a725 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3173,6 +3173,15 @@
*/
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ writeToParcelNoRecycle(parcel, flags);
+ // Since instances of this class are fetched via synchronous i.e. blocking
+ // calls in IPCs we always recycle as soon as the instance is marshaled.
+ recycle();
+ }
+
+ /** @hide */
+ @TestApi
+ public void writeToParcelNoRecycle(Parcel parcel, int flags) {
// Write bit set of indices of fields with values differing from default
long nonDefaultFields = 0;
int fieldIndex = 0; // index of the current field
@@ -3406,10 +3415,6 @@
+ " vs " + fieldIndex);
}
}
-
- // Since instances of this class are fetched via synchronous i.e. blocking
- // calls in IPCs we always recycle as soon as the instance is marshaled.
- recycle();
}
/**
@@ -3557,7 +3562,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
- if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index dac1998..8b64bad 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -20,6 +20,7 @@
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Bundle;
@@ -41,13 +44,23 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Choreographer;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -58,9 +71,10 @@
import java.util.List;
import java.util.Objects;
-// TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
+// TODO: use java.lang.ref.Cleaner once Android supports Java 9
+
/**
* The {@link AutofillManager} provides ways for apps and custom views to integrate with the
* Autofill Framework lifecycle.
@@ -178,7 +192,6 @@
private static final String STATE_TAG = "android:state";
private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
-
/** @hide */ public static final int ACTION_START_SESSION = 1;
/** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
/** @hide */ public static final int ACTION_VIEW_EXITED = 3;
@@ -355,6 +368,10 @@
@GuardedBy("mLock")
private boolean mSaveOnFinish;
+ /** If compatibility mode is enabled - this is a bridge to interact with a11y */
+ @GuardedBy("mLock")
+ private CompatibilityBridge mCompatibilityBridge;
+
/** @hide */
public interface AutofillClient {
/**
@@ -364,13 +381,13 @@
* @param intent The authentication intent.
* @param fillInIntent The authentication fill-in intent.
*/
- void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
+ void autofillClientAuthenticate(int authenticationId, IntentSender intent,
Intent fillInIntent);
/**
* Tells the client this manager has state to be reset.
*/
- void autofillCallbackResetableStateAvailable();
+ void autofillClientResetableStateAvailable();
/**
* Request showing the autofill UI.
@@ -382,7 +399,7 @@
* @param presenter The presenter that controls the fill UI window.
* @return Whether the UI was shown.
*/
- boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
+ boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
@Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
/**
@@ -390,21 +407,28 @@
*
* @return Whether the UI was hidden.
*/
- boolean autofillCallbackRequestHideFillUi();
+ boolean autofillClientRequestHideFillUi();
+
+ /**
+ * Gets whether the fill UI is currenlty being shown.
+ *
+ * @return Whether the fill UI is currently being shown
+ */
+ boolean autofillClientIsFillUiShowing();
/**
* Checks if views are currently attached and visible.
*
* @return And array with {@code true} iff the view is attached or visible
*/
- @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
+ @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
/**
* Checks is the client is currently visible as understood by autofill.
*
* @return {@code true} if the client is currently visible
*/
- boolean isVisibleForAutofill();
+ boolean autofillClientIsVisibleForAutofill();
/**
* Client might disable enter/exit event e.g. when activity is paused.
@@ -414,30 +438,51 @@
/**
* Finds views by traversing the hierarchies of the client.
*
- * @param viewIds The autofill ids of the views to find
+ * @param autofillIds The autofill ids of the views to find
*
* @return And array containing the views (empty if no views found).
*/
- @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
+ @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
+ @NonNull AutofillId[] autofillIds);
/**
* Finds a view by traversing the hierarchies of the client.
*
- * @param viewId The autofill id of the views to find
+ * @param autofillId The autofill id of the views to find
*
* @return The view, or {@code null} if not found
*/
- @Nullable View findViewByAutofillIdTraversal(int viewId);
+ @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
+
+ /**
+ * Finds a view by a11y id in a given client window.
+ *
+ * @param viewId The accessibility id of the views to find
+ * @param windowId The accessibility window id where to search
+ *
+ * @return The view, or {@code null} if not found
+ */
+ @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
/**
* Runs the specified action on the UI thread.
*/
- void runOnUiThread(Runnable action);
+ void autofillClientRunOnUiThread(Runnable action);
/**
* Gets the complete component name of this client.
*/
- ComponentName getComponentName();
+ ComponentName autofillClientGetComponentName();
+
+ /**
+ * Gets the activity token
+ */
+ @Nullable IBinder autofillClientGetActivityToken();
+
+ /**
+ * @return Whether compatibility mode is enabled.
+ */
+ boolean autofillIsCompatibilityModeEnabled();
}
/**
@@ -449,6 +494,19 @@
}
/**
+ * @hide
+ */
+ public void enableCompatibilityMode() {
+ synchronized (mLock) {
+ // The accessibility manager is a singleton so we may need to plug
+ // different bridge based on which activity is currently focused
+ // in the current process. Since compat would be rarely used, just
+ // create and register a new instance every time.
+ mCompatibilityBridge = new CompatibilityBridge();
+ }
+ }
+
+ /**
* Restore state after activity lifecycle
*
* @param savedInstanceState The state to be restored
@@ -477,7 +535,8 @@
if (client != null) {
try {
final boolean sessionWasRestored = mService.restoreSession(mSessionId,
- mContext.getActivityToken(), mServiceClient.asBinder());
+ client.autofillClientGetActivityToken(),
+ mServiceClient.asBinder());
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -488,7 +547,7 @@
Log.d(TAG, "session " + mSessionId + " was restored");
}
- client.autofillCallbackResetableStateAvailable();
+ client.autofillClientResetableStateAvailable();
}
} catch (RemoteException e) {
Log.e(TAG, "Could not figure out if there was an autofill session", e);
@@ -501,22 +560,29 @@
/**
* Called once the client becomes visible.
*
- * @see AutofillClient#isVisibleForAutofill()
+ * @see AutofillClient#autofillClientIsVisibleForAutofill()
*
* {@hide}
*/
public void onVisibleForAutofill() {
- synchronized (mLock) {
- if (mEnabled && isActiveLocked() && mTrackedViews != null) {
- mTrackedViews.onVisibleForAutofillLocked();
+ // This gets called when the client just got visible at which point the visibility
+ // of the tracked views may not have been computed (due to a pending layout, etc).
+ // While generally we have no way to know when the UI has settled. We will evaluate
+ // the tracked views state at the end of next frame to guarantee that everything
+ // that may need to be laid out is laid out.
+ Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
+ synchronized (mLock) {
+ if (mEnabled && isActiveLocked() && mTrackedViews != null) {
+ mTrackedViews.onVisibleForAutofillChangedLocked();
+ }
}
- }
+ }, null);
}
/**
* Called once the client becomes invisible.
*
- * @see AutofillClient#isVisibleForAutofill()
+ * @see AutofillClient#autofillClientIsVisibleForAutofill()
*
* {@hide}
*/
@@ -551,6 +617,14 @@
}
/**
+ * @hide
+ */
+ @GuardedBy("mLock")
+ public boolean isCompatibilityModeEnabledLocked() {
+ return mCompatibilityBridge != null;
+ }
+
+ /**
* Checks whether autofill is enabled for the current user.
*
* <p>Typically used to determine whether the option to explicitly request autofill should
@@ -636,6 +710,7 @@
notifyViewEntered(view, 0);
}
+ @GuardedBy("mLock")
private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
if (isDisabledByServiceLocked()) {
if (sVerbose) {
@@ -653,7 +728,7 @@
private boolean isClientVisibleForAutofillLocked() {
final AutofillClient client = getClient();
- return client != null && client.isVisibleForAutofill();
+ return client != null && client.autofillClientIsVisibleForAutofill();
}
private boolean isClientDisablingEnterExitEvent() {
@@ -676,6 +751,7 @@
}
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ @GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
@@ -719,6 +795,7 @@
}
}
+ @GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
ensureServiceClientAddedIfNeededLocked();
@@ -820,6 +897,7 @@
}
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ @GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
int flags) {
AutofillCallback callback = null;
@@ -863,6 +941,7 @@
}
}
+ @GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
ensureServiceClientAddedIfNeededLocked();
@@ -1014,6 +1093,7 @@
}
}
+ @GuardedBy("mLock")
private void commitLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
@@ -1042,6 +1122,7 @@
}
}
+ @GuardedBy("mLock")
private void cancelLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
@@ -1305,11 +1386,13 @@
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
+ @GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
- + ", flags=" + flags + ", state=" + getStateAsStringLocked());
+ + ", flags=" + flags + ", state=" + getStateAsStringLocked()
+ + ", compatMode=" + isCompatibilityModeEnabledLocked());
}
if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
if (sVerbose) {
@@ -1320,20 +1403,21 @@
}
try {
final AutofillClient client = getClient();
- if (client == null) return; // NOTE: getClient() already logd it..
+ if (client == null) return; // NOTE: getClient() already logged it..
- mSessionId = mService.startSession(mContext.getActivityToken(),
+ mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
- mCallback != null, flags, client.getComponentName());
+ mCallback != null, flags, client.autofillClientGetComponentName());
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
}
- client.autofillCallbackResetableStateAvailable();
+ client.autofillClientResetableStateAvailable();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ @GuardedBy("mLock")
private void finishSessionLocked() {
if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
@@ -1348,6 +1432,7 @@
resetSessionLocked();
}
+ @GuardedBy("mLock")
private void cancelSessionLocked() {
if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
@@ -1362,6 +1447,7 @@
resetSessionLocked();
}
+ @GuardedBy("mLock")
private void resetSessionLocked() {
mSessionId = NO_SESSION;
mState = STATE_UNKNOWN;
@@ -1370,6 +1456,7 @@
mSaveTriggerId = null;
}
+ @GuardedBy("mLock")
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
int flags) {
if (sVerbose && action != ACTION_VIEW_EXITED) {
@@ -1383,14 +1470,16 @@
final AutofillClient client = getClient();
if (client == null) return; // NOTE: getClient() already logd it..
- final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
+ final int newId = mService.updateOrRestartSession(
+ client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
- mCallback != null, flags, client.getComponentName(), mSessionId, action);
+ mCallback != null, flags, client.autofillClientGetComponentName(),
+ mSessionId, action);
if (newId != mSessionId) {
if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
mSessionId = newId;
mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
- client.autofillCallbackResetableStateAvailable();
+ client.autofillClientResetableStateAvailable();
}
} else {
mService.updateSession(mSessionId, id, bounds, value, action, flags,
@@ -1402,6 +1491,7 @@
}
}
+ @GuardedBy("mLock")
private void ensureServiceClientAddedIfNeededLocked() {
if (getClient() == null) {
return;
@@ -1489,7 +1579,7 @@
AutofillClient client = getClient();
if (client != null) {
- if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
+ if (client.autofillClientRequestShowFillUi(anchor, width, height,
anchorBounds, presenter) && mCallback != null) {
callback = mCallback;
}
@@ -1516,7 +1606,7 @@
// clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
// before onAuthenticationResult()
mOnInvisibleCalled = false;
- client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
+ client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
}
}
}
@@ -1587,7 +1677,8 @@
final int itemCount = ids.size();
int numApplied = 0;
ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
- final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
+ final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
+ Helper.toArray(ids));
for (int i = 0; i < itemCount; i++) {
final AutofillId id = ids.get(i);
@@ -1758,7 +1849,7 @@
// service being uninstalled and the UI being dismissed.
AutofillClient client = getClient();
if (client != null) {
- if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
+ if (client.autofillClientRequestHideFillUi() && mCallback != null) {
callback = mCallback;
}
}
@@ -1807,35 +1898,6 @@
}
/**
- * Get an array of viewIds from a List of {@link AutofillId}.
- *
- * @param autofillIds The autofill ids to convert
- *
- * @return The array of viewIds.
- */
- // TODO: move to Helper as static method
- @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
- final int numIds = autofillIds.length;
- final int[] viewIds = new int[numIds];
- for (int i = 0; i < numIds; i++) {
- viewIds[i] = autofillIds[i].getViewId();
- }
-
- return viewIds;
- }
-
- // TODO: move to Helper as static method
- @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
- final int numIds = autofillIds.size();
- final int[] viewIds = new int[numIds];
- for (int i = 0; i < numIds; i++) {
- viewIds[i] = autofillIds.get(i).getViewId();
- }
-
- return viewIds;
- }
-
- /**
* Find a single view by its id.
*
* @param autofillId The autofill id of the view
@@ -1844,12 +1906,10 @@
*/
private View findView(@NonNull AutofillId autofillId) {
final AutofillClient client = getClient();
-
- if (client == null) {
- return null;
+ if (client != null) {
+ return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
}
-
- return client.findViewByAutofillIdTraversal(autofillId.getViewId());
+ return null;
}
/** @hide */
@@ -1895,10 +1955,13 @@
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
+ pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
+ isCompatibilityModeEnabledLocked());
pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
pw.print(" verbose: "); pw.println(sVerbose);
}
+ @GuardedBy("mLock")
private String getStateAsStringLocked() {
switch (mState) {
case STATE_UNKNOWN:
@@ -1916,14 +1979,17 @@
}
}
+ @GuardedBy("mLock")
private boolean isActiveLocked() {
return mState == STATE_ACTIVE;
}
+ @GuardedBy("mLock")
private boolean isDisabledByServiceLocked() {
return mState == STATE_DISABLED_BY_SERVICE;
}
+ @GuardedBy("mLock")
private boolean isFinishedLocked() {
return mState == STATE_FINISHED;
}
@@ -1934,7 +2000,219 @@
if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
return;
}
- client.runOnUiThread(runnable);
+ client.autofillClientRunOnUiThread(runnable);
+ }
+
+ /**
+ * Implementation of the accessibility based compatibility.
+ */
+ private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
+ @GuardedBy("mLock")
+ private final Rect mFocusedBounds = new Rect();
+ @GuardedBy("mLock")
+ private final Rect mTempBounds = new Rect();
+
+ @GuardedBy("mLock")
+ private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ @GuardedBy("mLock")
+ private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
+
+ // Need to report a fake service in case a11y clients check the service list
+ @NonNull
+ @GuardedBy("mLock")
+ AccessibilityServiceInfo mCompatServiceInfo;
+
+ CompatibilityBridge() {
+ final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
+ am.setAccessibilityPolicy(this);
+ }
+
+ private AccessibilityServiceInfo getCompatServiceInfo() {
+ synchronized (mLock) {
+ if (mCompatServiceInfo != null) {
+ return mCompatServiceInfo;
+ }
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName("android",
+ "com.android.server.autofill.AutofillCompatAccessibilityService"));
+ final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
+ intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+ try {
+ mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Cannot find compat autofill service:" + intent);
+ throw new IllegalStateException("Cannot find compat autofill service");
+ }
+ return mCompatServiceInfo;
+ }
+ }
+
+ @Override
+ public boolean isEnabled(boolean accessibilityEnabled) {
+ return true;
+ }
+
+ @Override
+ public int getRelevantEventTypes(int relevantEventTypes) {
+ return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+ | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+ }
+
+ @Override
+ public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+ List<AccessibilityServiceInfo> installedServices) {
+ if (installedServices == null) {
+ installedServices = new ArrayList<>();
+ }
+ installedServices.add(getCompatServiceInfo());
+ return installedServices;
+ }
+
+ @Override
+ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+ int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
+ if (enabledService == null) {
+ enabledService = new ArrayList<>();
+ }
+ enabledService.add(getCompatServiceInfo());
+ return enabledService;
+ }
+
+ @Override
+ public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
+ boolean accessibilityEnabled, int relevantEventTypes) {
+ switch (event.getEventType()) {
+ case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
+ synchronized (mLock) {
+ if (mFocusedWindowId == event.getWindowId()
+ && mFocusedNodeId == event.getSourceNodeId()) {
+ return event;
+ }
+ if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+ && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
+ notifyViewExited(mFocusedWindowId, mFocusedNodeId);
+ mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
+ mFocusedBounds.set(0, 0, 0, 0);
+ }
+ final int windowId = event.getWindowId();
+ final long nodeId = event.getSourceNodeId();
+ if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
+ mFocusedWindowId = windowId;
+ mFocusedNodeId = nodeId;
+ }
+ }
+ } break;
+
+ case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
+ synchronized (mLock) {
+ if (mFocusedWindowId == event.getWindowId()
+ && mFocusedNodeId == event.getSourceNodeId()) {
+ notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
+ }
+ }
+ } break;
+
+ case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+ final AutofillClient client = getClient();
+ if (client != null) {
+ synchronized (mLock) {
+ if (client.autofillClientIsFillUiShowing()) {
+ notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
+ }
+ updateTrackedViewsLocked();
+ }
+ }
+ } break;
+ }
+
+ return accessibilityEnabled ? event : null;
+ }
+
+ private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
+ final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+ if (!isVirtualNode(virtualId)) {
+ return false;
+ }
+ final View view = findViewByAccessibilityId(windowId, nodeId);
+ if (view == null) {
+ return false;
+ }
+ final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
+ if (node == null) {
+ return false;
+ }
+ if (!node.isEditable()) {
+ return false;
+ }
+ final Rect newBounds = mTempBounds;
+ node.getBoundsInScreen(newBounds);
+ if (newBounds.equals(focusedBounds)) {
+ return false;
+ }
+ focusedBounds.set(newBounds);
+ AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
+ return true;
+ }
+
+ private void notifyViewExited(int windowId, long nodeId) {
+ final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+ if (!isVirtualNode(virtualId)) {
+ return;
+ }
+ final View view = findViewByAccessibilityId(windowId, nodeId);
+ if (view == null) {
+ return;
+ }
+ AutofillManager.this.notifyViewExited(view, virtualId);
+ }
+
+ private void notifyValueChanged(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.notifyValueChanged(view, virtualId,
+ AutofillValue.forText(node.getText()));
+ }
+
+ @GuardedBy("mLock")
+ private void updateTrackedViewsLocked() {
+ if (mTrackedViews != null) {
+ mTrackedViews.onVisibleForAutofillChangedLocked();
+ }
+ }
+
+ private View findViewByAccessibilityId(int windowId, long nodeId) {
+ final AutofillClient client = getClient();
+ if (client == null) {
+ return null;
+ }
+ final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
+ return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
+ }
+
+ private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
+ final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+ if (provider == null) {
+ return null;
+ }
+ return provider.createAccessibilityNodeInfo(virtualId);
+ }
+
+ private boolean isVirtualNode(int nodeId) {
+ return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
+ && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ }
}
/**
@@ -2013,11 +2291,11 @@
*/
TrackedViews(@Nullable AutofillId[] trackedIds) {
final AutofillClient client = getClient();
- if (trackedIds != null && client != null) {
+ if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
final boolean[] isVisible;
- if (client.isVisibleForAutofill()) {
- isVisible = client.getViewVisibility(getViewIds(trackedIds));
+ if (client.autofillClientIsVisibleForAutofill()) {
+ isVisible = client.autofillClientGetViewVisibility(trackedIds);
} else {
// All false
isVisible = new boolean[trackedIds.length];
@@ -2052,9 +2330,10 @@
* @param id the id of the view/virtual view whose visibility changed.
* @param isVisible visible if the view is visible in the view hierarchy.
*/
+ @GuardedBy("mLock")
void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
if (sDebug) {
- Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
+ Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
+ isVisible);
}
@@ -2083,9 +2362,10 @@
/**
* Called once the client becomes visible.
*
- * @see AutofillClient#isVisibleForAutofill()
+ * @see AutofillClient#autofillClientIsVisibleForAutofill()
*/
- void onVisibleForAutofillLocked() {
+ @GuardedBy("mLock")
+ void onVisibleForAutofillChangedLocked() {
// The visibility of the views might have changed while the client was not be visible,
// hence update the visibility state for all views.
AutofillClient client = getClient();
@@ -2095,8 +2375,8 @@
if (mInvisibleTrackedIds != null) {
final ArrayList<AutofillId> orderedInvisibleIds =
new ArrayList<>(mInvisibleTrackedIds);
- final boolean[] isVisible = client.getViewVisibility(
- getViewIds(orderedInvisibleIds));
+ final boolean[] isVisible = client.autofillClientGetViewVisibility(
+ Helper.toArray(orderedInvisibleIds));
final int numInvisibleTrackedIds = orderedInvisibleIds.size();
for (int i = 0; i < numInvisibleTrackedIds; i++) {
@@ -2116,8 +2396,8 @@
if (mVisibleTrackedIds != null) {
final ArrayList<AutofillId> orderedVisibleIds =
new ArrayList<>(mVisibleTrackedIds);
- final boolean[] isVisible = client.getViewVisibility(
- getViewIds(orderedVisibleIds));
+ final boolean[] isVisible = client.autofillClientGetViewVisibility(
+ Helper.toArray(orderedVisibleIds));
final int numVisibleTrackedIds = orderedVisibleIds.size();
for (int i = 0; i < numVisibleTrackedIds; i++) {
diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java
index fc5d306..155fe72 100644
--- a/core/java/android/view/autofill/AutofillManagerInternal.java
+++ b/core/java/android/view/autofill/AutofillManagerInternal.java
@@ -15,6 +15,9 @@
*/
package android.view.autofill;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+
/**
* Autofill Manager local system service interface.
*
@@ -26,4 +29,14 @@
* Notifies the manager that the back key was pressed.
*/
public abstract void onBackKeyPressed();
+
+ /**
+ * Gets whether compatibility mode is enabled for a package
+ *
+ * @param packageName The package for which to query.
+ * @param versionCode The package version code.
+ * @param userId The user id for which to query.
+ */
+ public abstract boolean isCompatibilityModeRequested(@NonNull String packageName,
+ long versionCode, @UserIdInt int userId);
}
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index 4b2c53c..48d0dbb 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.util.Collection;
+
/** @hide */
public final class Helper {
@@ -59,6 +61,20 @@
builder.append(" ]");
}
+ /**
+ * Convers a collaction of {@link AutofillId AutofillIds} to an array.
+ * @param collection The collection.
+ * @return The array.
+ */
+ public static @NonNull AutofillId[] toArray(Collection<AutofillId> collection) {
+ if (collection == null) {
+ return new AutofillId[0];
+ }
+ final AutofillId[] array = new AutofillId[collection.size()];
+ collection.toArray(array);
+ return array;
+ }
+
private Helper() {
throw new UnsupportedOperationException("contains static members only");
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7db5c32..9de26a8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
@@ -1178,8 +1179,9 @@
}
}
- /*
+ /**
* This method toggles the input method window display.
+ *
* If the input window is already displayed, it gets hidden.
* If not the input window will be displayed.
* @param showFlags Provides additional operating flags. May be
@@ -1188,7 +1190,6 @@
* @param hideFlags Provides additional operating flags. May be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
- * @hide
*/
public void toggleSoftInput(int showFlags, int hideFlags) {
if (mCurMethod != null) {
@@ -2137,6 +2138,26 @@
}
/**
+ * A test API for CTS to make sure that {@link #showInputMethodPicker()} works as expected.
+ *
+ * <p>When customizing the implementation of {@link #showInputMethodPicker()} API, make sure
+ * that this test API returns when and only while and only while
+ * {@link #showInputMethodPicker()} is showing UI. Otherwise your OS implementation may not
+ * pass CTS.</p>
+ *
+ * @return {@code true} while and only while {@link #showInputMethodPicker()} is showing UI.
+ * @hide
+ */
+ @TestApi
+ public boolean isInputMethodPickerShown() {
+ try {
+ return mService.isInputMethodPickerShownForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Show the settings for enabling subtypes of the specified input method.
* @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
* subtypes of all input methods will be shown.
diff --git a/core/java/android/view/textclassifier/SmartSelection.java b/core/java/android/view/textclassifier/SmartSelection.java
index 8edf97e..69c38ee 100644
--- a/core/java/android/view/textclassifier/SmartSelection.java
+++ b/core/java/android/view/textclassifier/SmartSelection.java
@@ -108,9 +108,9 @@
}
/**
- * Returns the language of the model.
+ * Returns a comma separated list of locales supported by the model as BCP 47 tags.
*/
- public static String getLanguage(int fd) {
+ public static String getLanguages(int fd) {
return nativeGetLanguage(fd);
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9f389ba..fc03493 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -27,8 +27,10 @@
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
+import android.os.UserManager;
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -56,6 +58,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -99,11 +102,9 @@
private final Object mLock = new Object();
@GuardedBy("mLock") // Do not access outside this lock.
- private Map<Locale, String> mModelFilePaths;
+ private List<ModelFile> mAllModelFiles;
@GuardedBy("mLock") // Do not access outside this lock.
- private Locale mLocale;
- @GuardedBy("mLock") // Do not access outside this lock.
- private int mVersion;
+ private ModelFile mModel;
@GuardedBy("mLock") // Do not access outside this lock.
private SmartSelection mSmartSelection;
@@ -279,18 +280,18 @@
private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
- final Locale locale = findBestSupportedLocaleLocked(localeList);
- if (locale == null) {
- throw new FileNotFoundException("No file for null locale");
+ final ModelFile bestModel = findBestModelLocked(localeList);
+ if (bestModel == null) {
+ throw new FileNotFoundException("No model for " + localeList.toLanguageTags());
}
- if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
+ if (mSmartSelection == null || !Objects.equals(mModel, bestModel)) {
+ Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
destroySmartSelectionIfExistsLocked();
- final ParcelFileDescriptor fd = getFdLocked(locale);
- final int modelFd = fd.getFd();
- mVersion = SmartSelection.getVersion(modelFd);
- mSmartSelection = new SmartSelection(modelFd);
+ final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
+ new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
+ mSmartSelection = new SmartSelection(fd.getFd());
closeAndLogError(fd);
- mLocale = locale;
+ mModel = bestModel;
}
return mSmartSelection;
}
@@ -298,74 +299,8 @@
private String getSignature(String text, int start, int end) {
synchronized (mLock) {
- return DefaultLogger.createSignature(text, start, end, mContext, mVersion, mLocale);
- }
- }
-
- @GuardedBy("mLock") // Do not call outside this lock.
- private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
- ParcelFileDescriptor updateFd;
- int updateVersion = -1;
- try {
- updateFd = ParcelFileDescriptor.open(
- new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
- if (updateFd != null) {
- updateVersion = SmartSelection.getVersion(updateFd.getFd());
- }
- } catch (FileNotFoundException e) {
- updateFd = null;
- }
- ParcelFileDescriptor factoryFd;
- int factoryVersion = -1;
- try {
- final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale);
- if (factoryModelFilePath != null) {
- factoryFd = ParcelFileDescriptor.open(
- new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
- if (factoryFd != null) {
- factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
- }
- } else {
- factoryFd = null;
- }
- } catch (FileNotFoundException e) {
- factoryFd = null;
- }
-
- if (updateFd == null) {
- if (factoryFd != null) {
- return factoryFd;
- } else {
- throw new FileNotFoundException(
- String.format(Locale.US, "No model file found for %s", locale));
- }
- }
-
- final int updateFdInt = updateFd.getFd();
- final boolean localeMatches = Objects.equals(
- locale.getLanguage().trim().toLowerCase(),
- SmartSelection.getLanguage(updateFdInt).trim().toLowerCase());
- if (factoryFd == null) {
- if (localeMatches) {
- return updateFd;
- } else {
- closeAndLogError(updateFd);
- throw new FileNotFoundException(
- String.format(Locale.US, "No model file found for %s", locale));
- }
- }
-
- if (!localeMatches) {
- closeAndLogError(updateFd);
- return factoryFd;
- }
-
- if (updateVersion > factoryVersion) {
- closeAndLogError(factoryFd);
- return updateFd;
- } else {
- closeAndLogError(updateFd);
- return factoryFd;
+ return DefaultLogger.createSignature(text, start, end, mContext, mModel.getVersion(),
+ mModel.getSupportedLocales());
}
}
@@ -377,60 +312,66 @@
}
}
+ /**
+ * Finds the most appropriate model to use for the given target locale list.
+ *
+ * The basic logic is: we ignore all models that don't support any of the target locales. For
+ * the remaining candidates, we take the update model unless its version number is lower than
+ * the factory version. It's assumed that factory models do not have overlapping locale ranges
+ * and conflict resolution between these models hence doesn't matter.
+ */
@GuardedBy("mLock") // Do not call outside this lock.
@Nullable
- private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
+ private ModelFile findBestModelLocked(LocaleList localeList) {
// Specified localeList takes priority over the system default, so it is listed first.
final String languages = localeList.isEmpty()
? LocaleList.getDefault().toLanguageTags()
: localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
- final List<Locale> supportedLocales =
- new ArrayList<>(getFactoryModelFilePathsLocked().keySet());
- final Locale updatedModelLocale = getUpdatedModelLocale();
- if (updatedModelLocale != null) {
- supportedLocales.add(updatedModelLocale);
+ ModelFile bestModel = null;
+ int bestModelVersion = -1;
+ for (ModelFile model : listAllModelsLocked()) {
+ if (model.isAnyLanguageSupported(languageRangeList)) {
+ if (model.getVersion() >= bestModelVersion) {
+ bestModel = model;
+ bestModelVersion = model.getVersion();
+ }
+ }
}
- return Locale.lookup(languageRangeList, supportedLocales);
+ return bestModel;
}
+ /** Returns a list of all model files available, in order of precedence. */
@GuardedBy("mLock") // Do not call outside this lock.
- private Map<Locale, String> getFactoryModelFilePathsLocked() {
- if (mModelFilePaths == null) {
- final Map<Locale, String> modelFilePaths = new HashMap<>();
+ private List<ModelFile> listAllModelsLocked() {
+ if (mAllModelFiles == null) {
+ final List<ModelFile> allModels = new ArrayList<>();
+ // The update model has the highest precedence.
+ if (new File(UPDATED_MODEL_FILE_PATH).exists()) {
+ final ModelFile updatedModel = ModelFile.fromPath(UPDATED_MODEL_FILE_PATH);
+ if (updatedModel != null) {
+ allModels.add(updatedModel);
+ }
+ }
+ // Factory models should never have overlapping locales, so the order doesn't matter.
final File modelsDir = new File(MODEL_DIR);
if (modelsDir.exists() && modelsDir.isDirectory()) {
- final File[] models = modelsDir.listFiles();
+ final File[] modelFiles = modelsDir.listFiles();
final Pattern modelFilenamePattern = Pattern.compile(MODEL_FILE_REGEX);
- final int size = models.length;
- for (int i = 0; i < size; i++) {
- final File modelFile = models[i];
+ for (File modelFile : modelFiles) {
final Matcher matcher = modelFilenamePattern.matcher(modelFile.getName());
if (matcher.matches() && modelFile.isFile()) {
- final String language = matcher.group(1);
- final Locale locale = Locale.forLanguageTag(language);
- modelFilePaths.put(locale, modelFile.getAbsolutePath());
+ final ModelFile model = ModelFile.fromPath(modelFile.getAbsolutePath());
+ if (model != null) {
+ allModels.add(model);
+ }
}
}
}
- mModelFilePaths = modelFilePaths;
+ mAllModelFiles = allModels;
}
- return mModelFilePaths;
- }
-
- @Nullable
- private Locale getUpdatedModelLocale() {
- try {
- final ParcelFileDescriptor updateFd = ParcelFileDescriptor.open(
- new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
- final Locale locale = Locale.forLanguageTag(
- SmartSelection.getLanguage(updateFd.getFd()));
- closeAndLogError(updateFd);
- return locale;
- } catch (FileNotFoundException e) {
- return null;
- }
+ return mAllModelFiles;
}
private TextClassification createClassificationResult(
@@ -520,6 +461,95 @@
}
/**
+ * Describes TextClassifier model files on disk.
+ */
+ private static final class ModelFile {
+
+ private final String mPath;
+ private final String mName;
+ private final int mVersion;
+ private final List<Locale> mSupportedLocales;
+
+ /** Returns null if the path did not point to a compatible model. */
+ static @Nullable ModelFile fromPath(String path) {
+ final File file = new File(path);
+ try {
+ final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
+ file, ParcelFileDescriptor.MODE_READ_ONLY);
+ final int version = SmartSelection.getVersion(modelFd.getFd());
+ final String supportedLocalesStr = SmartSelection.getLanguages(modelFd.getFd());
+ if (supportedLocalesStr.isEmpty()) {
+ Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
+ return null;
+ }
+ final List<Locale> supportedLocales = new ArrayList<>();
+ for (String langTag : supportedLocalesStr.split(",")) {
+ supportedLocales.add(Locale.forLanguageTag(langTag));
+ }
+ closeAndLogError(modelFd);
+ return new ModelFile(path, file.getName(), version, supportedLocales);
+ } catch (FileNotFoundException e) {
+ Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e);
+ return null;
+ }
+ }
+
+ /** The absolute path to the model file. */
+ String getPath() {
+ return mPath;
+ }
+
+ /** A name to use for signature generation. Effectively the name of the model file. */
+ String getName() {
+ return mName;
+ }
+
+ /** Returns the version tag in the model's metadata. */
+ int getVersion() {
+ return mVersion;
+ }
+
+ /** Returns whether the language supports any language in the given ranges. */
+ boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
+ return Locale.lookup(languageRanges, mSupportedLocales) != null;
+ }
+
+ /** All locales supported by the model. */
+ List<Locale> getSupportedLocales() {
+ return Collections.unmodifiableList(mSupportedLocales);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other == null || !ModelFile.class.isAssignableFrom(other.getClass())) {
+ return false;
+ } else {
+ final ModelFile otherModel = (ModelFile) other;
+ return mPath.equals(otherModel.mPath);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringJoiner localesJoiner = new StringJoiner(",");
+ for (Locale locale : mSupportedLocales) {
+ localesJoiner.add(locale.toLanguageTag());
+ }
+ return String.format(Locale.US, "ModelFile { path=%s name=%s version=%d locales=%s }",
+ mPath, mName, mVersion, localesJoiner.toString());
+ }
+
+ private ModelFile(String path, String name, int version, List<Locale> supportedLocales) {
+ mPath = path;
+ mName = name;
+ mVersion = version;
+ mSupportedLocales = supportedLocales;
+ }
+ }
+
+ /**
* Creates intents based on the classification type.
*/
static final class IntentFactory {
@@ -541,7 +571,7 @@
case TextClassifier.TYPE_EMAIL:
return createForEmail(text);
case TextClassifier.TYPE_PHONE:
- return createForPhone(text);
+ return createForPhone(context, text);
case TextClassifier.TYPE_ADDRESS:
return createForAddress(text);
case TextClassifier.TYPE_URL:
@@ -573,15 +603,23 @@
}
@NonNull
- private static List<Intent> createForPhone(String text) {
- return Arrays.asList(
- new Intent(Intent.ACTION_DIAL)
- .setData(Uri.parse(String.format("tel:%s", text))),
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.PHONE, text),
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("smsto:%s", text))));
+ private static List<Intent> createForPhone(Context context, String text) {
+ final List<Intent> intents = new ArrayList<>();
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ final Bundle userRestrictions = userManager != null
+ ? userManager.getUserRestrictions() : new Bundle();
+ if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
+ intents.add(new Intent(Intent.ACTION_DIAL)
+ .setData(Uri.parse(String.format("tel:%s", text))));
+ }
+ intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.PHONE, text));
+ if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
+ intents.add(new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("smsto:%s", text))));
+ }
+ return intents;
}
@NonNull
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index ede5211..d866d13 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -198,7 +198,11 @@
/**
* Create a new TextLink.
*
- * @throws IllegalArgumentException if entityScores is null or empty.
+ * @param start The start index of the identified subsequence
+ * @param end The end index of the identified subsequence
+ * @param entityScores A mapping of entity type to confidence score
+ *
+ * @throws IllegalArgumentException if entityScores is null or empty
*/
TextLink(int start, int end, Map<String, Float> entityScores) {
Preconditions.checkNotNull(entityScores);
@@ -212,7 +216,7 @@
/**
* Returns the start index of this link in the original text.
*
- * @return the start index.
+ * @return the start index
*/
public int getStart() {
return mStart;
@@ -221,7 +225,7 @@
/**
* Returns the end index of this link in the original text.
*
- * @return the end index.
+ * @return the end index
*/
public int getEnd() {
return mEnd;
@@ -230,7 +234,7 @@
/**
* Returns the number of entity types that have confidence scores.
*
- * @return the entity count.
+ * @return the entity count
*/
public int getEntityCount() {
return mEntityScores.getEntities().size();
@@ -239,7 +243,7 @@
/**
* Returns the entity type at a given index. Entity types are sorted by confidence.
*
- * @return the entity type at the provided index.
+ * @return the entity type at the provided index
*/
@NonNull public @EntityType String getEntity(int index) {
return mEntityScores.getEntities().get(index);
@@ -248,7 +252,7 @@
/**
* Returns the confidence score for a particular entity type.
*
- * @param entityType the entity type.
+ * @param entityType the entity type
*/
public @FloatRange(from = 0.0, to = 1.0) float getConfidenceScore(
@EntityType String entityType) {
@@ -371,7 +375,7 @@
/**
* @return ordered list of locale preferences that can be used to disambiguate
- * the provided text.
+ * the provided text
*/
@Nullable
public LocaleList getDefaultLocales() {
@@ -389,7 +393,7 @@
/**
* @return the strategy for resolving conflictswhen applying generated links to text that
- * already have links.
+ * already have links
*
* @see #APPLY_STRATEGY_IGNORE
* @see #APPLY_STRATEGY_REPLACE
@@ -494,7 +498,7 @@
/**
* Create a new TextLinks.Builder.
*
- * @param fullText The full text to annotate with links.
+ * @param fullText The full text to annotate with links
*/
public Builder(@NonNull String fullText) {
mFullText = Preconditions.checkNotNull(fullText);
@@ -504,7 +508,7 @@
/**
* Adds a TextLink.
*
- * @return this instance.
+ * @return this instance
*
* @throws IllegalArgumentException if entityScores is null or empty.
*/
@@ -524,7 +528,7 @@
/**
* Constructs a TextLinks instance.
*
- * @return the constructed TextLinks.
+ * @return the constructed TextLinks
*/
public TextLinks build() {
return new TextLinks(mFullText, mLinks);
diff --git a/core/java/android/view/textclassifier/logging/DefaultLogger.java b/core/java/android/view/textclassifier/logging/DefaultLogger.java
index 6b84835..03a6d3a 100644
--- a/core/java/android/view/textclassifier/logging/DefaultLogger.java
+++ b/core/java/android/view/textclassifier/logging/DefaultLogger.java
@@ -17,7 +17,6 @@
package android.view.textclassifier.logging;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.metrics.LogMaker;
import android.util.Log;
@@ -27,8 +26,10 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.Preconditions;
+import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.StringJoiner;
/**
* Default Logger.
@@ -210,12 +211,16 @@
*/
public static String createSignature(
String text, int start, int end, Context context, int modelVersion,
- @Nullable Locale locale) {
+ List<Locale> locales) {
Preconditions.checkNotNull(text);
Preconditions.checkNotNull(context);
- final String modelName = (locale != null)
- ? String.format(Locale.US, "%s_v%d", locale.toLanguageTag(), modelVersion)
- : "";
+ Preconditions.checkNotNull(locales);
+ final StringJoiner localesJoiner = new StringJoiner(",");
+ for (Locale locale : locales) {
+ localesJoiner.add(locale.toLanguageTag());
+ }
+ final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(),
+ modelVersion);
final int hash = Objects.hash(text, start, end, context.getPackageName());
return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
}
@@ -242,9 +247,9 @@
static String getModelName(String signature) {
Preconditions.checkNotNull(signature);
- final int start = signature.indexOf("|");
+ final int start = signature.indexOf("|") + 1;
final int end = signature.indexOf("|", start);
- if (start >= 0 && end >= start) {
+ if (start >= 1 && end >= start) {
return signature.substring(start, end);
}
return "";
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7bb0db1..41ceb30 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1299,6 +1299,16 @@
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.resetTouchOffsets();
}
+
+ ensureNoSelectionIfNonSelectable();
+ }
+ }
+
+ private void ensureNoSelectionIfNonSelectable() {
+ // This could be the case if a TextLink has been tapped.
+ if (!mTextView.textCanBeSelected() && mTextView.hasSelection()) {
+ Selection.setSelection((Spannable) mTextView.getText(),
+ mTextView.length(), mTextView.length());
}
}
@@ -1382,6 +1392,8 @@
// Don't leave us in the middle of a batch edit. Same as in onFocusChanged
ensureEndedBatchEdit();
+
+ ensureNoSelectionIfNonSelectable();
}
}
@@ -1745,16 +1757,19 @@
highlight = null;
}
+ if (mSelectionActionModeHelper != null) {
+ mSelectionActionModeHelper.onDraw(canvas);
+ if (mSelectionActionModeHelper.isDrawingHighlight()) {
+ highlight = null;
+ }
+ }
+
if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
drawHardwareAccelerated(canvas, layout, highlight, highlightPaint,
cursorOffsetVertical);
} else {
layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
}
-
- if (mSelectionActionModeHelper != null) {
- mSelectionActionModeHelper.onDraw(canvas);
- }
}
private void drawHardwareAccelerated(Canvas canvas, Layout layout, Path highlight,
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 012b918..3aae849 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -904,6 +904,9 @@
}
}
+ /**
+ * @hide
+ */
@Override
protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
// Apply defaults, so as to remove UNDEFINED values
@@ -919,6 +922,9 @@
}
}
+ /**
+ * @hide
+ */
@Override
protected void onDebugDraw(Canvas canvas) {
Paint paint = new Paint();
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 1dc5b44..4b951fa 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -23,10 +23,12 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -53,7 +55,6 @@
import com.android.internal.R;
import java.io.IOException;
-import java.io.InputStream;
/**
* Displays image resources, for example {@link android.graphics.Bitmap}
@@ -946,21 +947,15 @@
}
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
- InputStream stream = null;
try {
- stream = mContext.getContentResolver().openInputStream(uri);
- return Drawable.createFromResourceStream(sCompatUseCorrectStreamDensity
- ? getResources() : null, null, stream, null);
- } catch (Exception e) {
+ Resources res = sCompatUseCorrectStreamDensity ? getResources() : null;
+ ImageDecoder.Source src = ImageDecoder.createSource(mContext.getContentResolver(),
+ uri, res);
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException e) {
Log.w(LOG_TAG, "Unable to open content: " + uri, e);
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- Log.w(LOG_TAG, "Unable to close content: " + uri, e);
- }
- }
}
} else {
return Drawable.createFromPath(uri.toString());
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 7a4c800..8836561 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -18,8 +18,10 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.annotation.UiThread;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
@@ -269,4 +271,46 @@
return mWindow.getContentView().findViewById(
com.android.internal.R.id.magnifier_image);
}
+
+ /**
+ * @return the content being currently displayed in the magnifier, as bitmap
+ *
+ * @hide
+ */
+ @TestApi
+ public Bitmap getContent() {
+ return mBitmap;
+ }
+
+ /**
+ * @return the position of the magnifier window relative to the screen
+ *
+ * @hide
+ */
+ @TestApi
+ public Rect getWindowPositionOnScreen() {
+ final int[] viewLocationOnScreen = new int[2];
+ mView.getLocationOnScreen(viewLocationOnScreen);
+ final int[] viewLocationInSurface = new int[2];
+ mView.getLocationInSurface(viewLocationInSurface);
+
+ final int left = mWindowCoords.x + viewLocationOnScreen[0] - viewLocationInSurface[0];
+ final int top = mWindowCoords.y + viewLocationOnScreen[1] - viewLocationInSurface[1];
+ return new Rect(left, top, left + mWindow.getWidth(), top + mWindow.getHeight());
+ }
+
+ /**
+ * @return the size of the magnifier window in dp
+ *
+ * @hide
+ */
+ @TestApi
+ public static PointF getMagnifierDefaultSize() {
+ final Resources resources = Resources.getSystem();
+ final float density = resources.getDisplayMetrics().density;
+ final PointF size = new PointF();
+ size.x = resources.getDimension(com.android.internal.R.dimen.magnifier_width) / density;
+ size.y = resources.getDimension(com.android.internal.R.dimen.magnifier_height) / density;
+ return size;
+ }
}
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 2e4cccf..7d556bf 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -19,12 +19,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.session.MediaController;
import android.media.update.ApiLoader;
-import android.media.update.FrameLayoutHelper;
import android.media.update.MediaControlView2Provider;
+import android.media.update.ViewGroupHelper;
import android.util.AttributeSet;
import java.lang.annotation.Retention;
@@ -50,9 +49,8 @@
* There is no separate method that handles the show/hide behavior for MediaControlView2. Instead,
* one can directly change the visibility of this view by calling View.setVisibility(int). The
* values supported are View.VISIBLE and View.GONE.
- * In addition, the following customizations are supported:
- * 1. Modify default timeout value of 2 seconds by calling setTimeout(long).
- * 2. Set focus to the play/pause button by calling requestPlayButtonFocus().
+ * In addition, the following customization is supported:
+ * Set focus to the play/pause button by calling requestPlayButtonFocus().
*
* <p>
* It is also possible to add custom buttons with custom icons and actions inside MediaControlView2.
@@ -62,7 +60,7 @@
* TODO PUBLIC API
* @hide
*/
-public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provider> {
+public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> {
/** @hide */
@IntDef({
BUTTON_PLAY_PAUSE,
@@ -156,18 +154,12 @@
public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
- super((instance, superProvider) ->
+ super((instance, superProvider, privateProvider) ->
ApiLoader.getProvider(context).createMediaControlView2(
- (MediaControlView2) instance, superProvider),
+ (MediaControlView2) instance, superProvider, privateProvider,
+ attrs, defStyleAttr, defStyleRes),
context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * @hide
- */
- @SystemApi
- public MediaControlView2Provider getProvider() {
- return mProvider;
+ mProvider.initialize(attrs, defStyleAttr, defStyleRes);
}
/**
@@ -178,13 +170,6 @@
}
/**
- * Returns whether the control view is currently shown or hidden.
- */
- public boolean isShowing() {
- return mProvider.isShowing_impl();
- }
-
- /**
* Changes the visibility state of an individual button. Default value is View.Visible.
*
* @param button the {@code Button} assigned to individual buttons
@@ -214,27 +199,8 @@
mProvider.requestPlayButtonFocus_impl();
}
- /**
- * Sets a new timeout value (in milliseconds) for showing MediaControlView2. The default value
- * is set as 2 seconds.
- * @param timeout the
- */
- public void setTimeout(long timeout) {
- mProvider.setTimeout_impl(timeout);
- }
-
- /**
- * Retrieves current timeout value (in milliseconds) for showing MediaControlView2. The default
- * value is set as 2 seconds.
- */
- public long getTimeout() {
- return mProvider.getTimeout_impl();
- }
-
@Override
- // TODO Move this method to ViewProvider
- public void onVisibilityAggregated(boolean isVisible) {
-
- mProvider.onVisibilityAggregated_impl(isVisible);
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mProvider.onLayout_impl(changed, l, t, r, b);
}
}
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 8f0d02f..2789bae 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -1,12 +1,9 @@
per-file TextView.java = siyamed@google.com
per-file TextView.java = nona@google.com
per-file TextView.java = clarabayarri@google.com
-per-file TextView.java = toki@google.com
per-file EditText.java = siyamed@google.com
per-file EditText.java = nona@google.com
per-file EditText.java = clarabayarri@google.com
-per-file EditText.java = toki@google.com
per-file Editor.java = siyamed@google.com
per-file Editor.java = nona@google.com
per-file Editor.java = clarabayarri@google.com
-per-file Editor.java = toki@google.com
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 2e354c1..e7a4c02 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -93,7 +93,7 @@
if (SMART_SELECT_ANIMATION_ENABLED) {
mSmartSelectSprite = new SmartSelectSprite(mTextView.getContext(),
- mTextView::invalidate);
+ editor.getTextView().mHighlightColor, mTextView::invalidate);
} else {
mSmartSelectSprite = null;
}
@@ -200,11 +200,15 @@
}
public void onDraw(final Canvas canvas) {
- if (mSmartSelectSprite != null) {
+ if (isDrawingHighlight() && mSmartSelectSprite != null) {
mSmartSelectSprite.draw(canvas);
}
}
+ public boolean isDrawingHighlight() {
+ return mSmartSelectSprite != null && mSmartSelectSprite.isAnimationActive();
+ }
+
private void cancelAsyncTask() {
if (mTextClassificationAsyncTask != null) {
mTextClassificationAsyncTask.cancel(true);
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index a391c6e..9a84f69 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -26,7 +26,6 @@
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
@@ -36,7 +35,6 @@
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.Shape;
import android.text.Layout;
-import android.util.TypedValue;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -54,21 +52,15 @@
final class SmartSelectSprite {
private static final int EXPAND_DURATION = 300;
- private static final int CORNER_DURATION = 150;
- private static final float STROKE_WIDTH_DP = 1.5F;
-
- // GBLUE700
- @ColorInt
- private static final int DEFAULT_STROKE_COLOR = 0xFF3367D6;
+ private static final int CORNER_DURATION = 50;
private final Interpolator mExpandInterpolator;
private final Interpolator mCornerInterpolator;
- private final float mStrokeWidth;
private Animator mActiveAnimator = null;
private final Runnable mInvalidator;
@ColorInt
- private final int mStrokeColor;
+ private final int mFillColor;
static final Comparator<RectF> RECTANGLE_COMPARATOR = Comparator
.<RectF>comparingDouble(e -> e.bottom)
@@ -124,26 +116,11 @@
return expansionDirection * -1;
}
- @Retention(SOURCE)
- @IntDef({RectangleBorderType.FIT, RectangleBorderType.OVERSHOOT})
- private @interface RectangleBorderType {
- /** A rectangle which, fully expanded, fits inside of its bounding rectangle. */
- int FIT = 0;
- /**
- * A rectangle which, when fully expanded, clips outside of its bounding rectangle so that
- * its edges no longer appear rounded.
- */
- int OVERSHOOT = 1;
- }
-
- private final float mStrokeWidth;
private final RectF mBoundingRectangle;
private float mRoundRatio = 1.0f;
private final @ExpansionDirection int mExpansionDirection;
- private final @RectangleBorderType int mRectangleBorderType;
private final RectF mDrawRect = new RectF();
- private final RectF mClipRect = new RectF();
private final Path mClipPath = new Path();
/** How offset the left edge of the rectangle is from the left side of the bounding box. */
@@ -159,13 +136,9 @@
private RoundedRectangleShape(
final RectF boundingRectangle,
final @ExpansionDirection int expansionDirection,
- final @RectangleBorderType int rectangleBorderType,
- final boolean inverted,
- final float strokeWidth) {
+ final boolean inverted) {
mBoundingRectangle = new RectF(boundingRectangle);
mBoundingWidth = boundingRectangle.width();
- mRectangleBorderType = rectangleBorderType;
- mStrokeWidth = strokeWidth;
mInverted = inverted && expansionDirection != ExpansionDirection.CENTER;
if (inverted) {
@@ -182,14 +155,8 @@
}
/*
- * In order to achieve the "rounded rectangle hits the wall" effect, the drawing needs to be
- * done in two passes. In this context, the wall is the bounding rectangle and in the first
- * pass we need to draw the rounded rectangle (expanded and with a corner radius as per
- * object properties) clipped by the bounding box. If the rounded rectangle expands outside
- * of the bounding box, one more pass needs to be done, as there will now be a hole in the
- * rounded rectangle where it "flattened" against the bounding box. In order to fill just
- * this hole, we need to draw the bounding box, but clip it with the rounded rectangle and
- * this will connect the missing pieces.
+ * In order to achieve the "rounded rectangle hits the wall" effect, we draw an expanding
+ * rounded rectangle that is clipped by the bounding box of the selected text.
*/
@Override
public void draw(Canvas canvas, Paint paint) {
@@ -201,31 +168,8 @@
final float adjustedCornerRadius = getAdjustedCornerRadius();
mDrawRect.set(mBoundingRectangle);
- mDrawRect.left = mBoundingRectangle.left + mLeftBoundary;
- mDrawRect.right = mBoundingRectangle.left + mRightBoundary;
-
- if (mRectangleBorderType == RectangleBorderType.OVERSHOOT) {
- mDrawRect.left -= cornerRadius / 2;
- mDrawRect.right += cornerRadius / 2;
- } else {
- switch (mExpansionDirection) {
- case ExpansionDirection.CENTER:
- break;
- case ExpansionDirection.LEFT:
- mDrawRect.right += cornerRadius;
- break;
- case ExpansionDirection.RIGHT:
- mDrawRect.left -= cornerRadius;
- break;
- }
- }
-
- canvas.save();
- mClipRect.set(mBoundingRectangle);
- mClipRect.inset(-mStrokeWidth / 2, -mStrokeWidth / 2);
- canvas.clipRect(mClipRect);
- canvas.drawRoundRect(mDrawRect, adjustedCornerRadius, adjustedCornerRadius, paint);
- canvas.restore();
+ mDrawRect.left = mBoundingRectangle.left + mLeftBoundary - cornerRadius / 2;
+ mDrawRect.right = mBoundingRectangle.left + mRightBoundary + cornerRadius / 2;
canvas.save();
mClipPath.reset();
@@ -272,11 +216,7 @@
}
private float getBoundingWidth() {
- if (mRectangleBorderType == RectangleBorderType.OVERSHOOT) {
- return (int) (mBoundingRectangle.width() + getCornerRadius());
- } else {
- return mBoundingRectangle.width();
- }
+ return (int) (mBoundingRectangle.width() + getCornerRadius());
}
}
@@ -388,19 +328,20 @@
}
/**
- * @param context the {@link Context} in which the animation will run
+ * @param context the {@link Context} in which the animation will run
+ * @param highlightColor the highlight color of the underlying {@link TextView}
* @param invalidator a {@link Runnable} which will be called every time the animation updates,
* indicating that the view drawing the animation should invalidate itself
*/
- SmartSelectSprite(final Context context, final Runnable invalidator) {
+ SmartSelectSprite(final Context context, @ColorInt int highlightColor,
+ final Runnable invalidator) {
mExpandInterpolator = AnimationUtils.loadInterpolator(
context,
android.R.interpolator.fast_out_slow_in);
mCornerInterpolator = AnimationUtils.loadInterpolator(
context,
android.R.interpolator.fast_out_linear_in);
- mStrokeWidth = dpToPixel(context, STROKE_WIDTH_DP);
- mStrokeColor = getStrokeColor(context);
+ mFillColor = highlightColor;
mInvalidator = Preconditions.checkNotNull(invalidator);
}
@@ -437,17 +378,14 @@
RectangleWithTextSelectionLayout centerRectangle = null;
int startingOffset = 0;
- int startingRectangleIndex = 0;
- for (int index = 0; index < rectangleCount; ++index) {
- final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
- destinationRectangles.get(index);
+ for (RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout :
+ destinationRectangles) {
final RectF rectangle = rectangleWithTextSelectionLayout.getRectangle();
if (contains(rectangle, start)) {
centerRectangle = rectangleWithTextSelectionLayout;
break;
}
startingOffset += rectangle.width();
- ++startingRectangleIndex;
}
if (centerRectangle == null) {
@@ -459,9 +397,6 @@
final @RoundedRectangleShape.ExpansionDirection int[] expansionDirections =
generateDirections(centerRectangle, destinationRectangles);
- final @RoundedRectangleShape.RectangleBorderType int[] rectangleBorderTypes =
- generateBorderTypes(rectangleCount);
-
for (int index = 0; index < rectangleCount; ++index) {
final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
destinationRectangles.get(index);
@@ -469,10 +404,8 @@
final RoundedRectangleShape shape = new RoundedRectangleShape(
rectangle,
expansionDirections[index],
- rectangleBorderTypes[index],
rectangleWithTextSelectionLayout.getTextSelectionLayout()
- == Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
- mStrokeWidth);
+ == Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT);
cornerAnimators.add(createCornerAnimator(shape, updateListener));
shapes.add(shape);
}
@@ -480,44 +413,23 @@
final RectangleList rectangleList = new RectangleList(shapes);
final ShapeDrawable shapeDrawable = new ShapeDrawable(rectangleList);
- final float startingOffsetLeft;
- final float startingOffsetRight;
-
- final RoundedRectangleShape startingRectangleShape = shapes.get(startingRectangleIndex);
- final float cornerRadius = startingRectangleShape.getCornerRadius();
- if (startingRectangleShape.mRectangleBorderType
- == RoundedRectangleShape.RectangleBorderType.FIT) {
- switch (startingRectangleShape.mExpansionDirection) {
- case RoundedRectangleShape.ExpansionDirection.LEFT:
- startingOffsetLeft = startingOffsetRight = startingOffset - cornerRadius / 2;
- break;
- case RoundedRectangleShape.ExpansionDirection.RIGHT:
- startingOffsetLeft = startingOffsetRight = startingOffset + cornerRadius / 2;
- break;
- case RoundedRectangleShape.ExpansionDirection.CENTER: // fall through
- default:
- startingOffsetLeft = startingOffset - cornerRadius / 2;
- startingOffsetRight = startingOffset + cornerRadius / 2;
- break;
- }
- } else {
- startingOffsetLeft = startingOffsetRight = startingOffset;
- }
-
final Paint paint = shapeDrawable.getPaint();
- paint.setColor(mStrokeColor);
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(mStrokeWidth);
+ paint.setColor(mFillColor);
+ paint.setStyle(Paint.Style.FILL);
mExistingRectangleList = rectangleList;
mExistingDrawable = shapeDrawable;
- mActiveAnimator = createAnimator(rectangleList, startingOffsetLeft, startingOffsetRight,
- cornerAnimators, updateListener,
- onAnimationEnd);
+ mActiveAnimator = createAnimator(rectangleList, startingOffset, startingOffset,
+ cornerAnimators, updateListener, onAnimationEnd);
mActiveAnimator.start();
}
+ /** Returns whether the sprite is currently animating. */
+ public boolean isAnimationActive() {
+ return mActiveAnimator != null && mActiveAnimator.isRunning();
+ }
+
private Animator createAnimator(
final RectangleList rectangleList,
final float startingOffsetLeft,
@@ -625,36 +537,6 @@
return result;
}
- private static @RoundedRectangleShape.RectangleBorderType int[] generateBorderTypes(
- final int numberOfRectangles) {
- final @RoundedRectangleShape.RectangleBorderType int[] result = new int[numberOfRectangles];
-
- for (int i = 1; i < result.length - 1; ++i) {
- result[i] = RoundedRectangleShape.RectangleBorderType.OVERSHOOT;
- }
-
- result[0] = RoundedRectangleShape.RectangleBorderType.FIT;
- result[result.length - 1] = RoundedRectangleShape.RectangleBorderType.FIT;
- return result;
- }
-
- private static float dpToPixel(final Context context, final float dp) {
- return TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- dp,
- context.getResources().getDisplayMetrics());
- }
-
- @ColorInt
- private static int getStrokeColor(final Context context) {
- final TypedValue typedValue = new TypedValue();
- final TypedArray array = context.obtainStyledAttributes(typedValue.data, new int[]{
- android.R.attr.colorControlActivated});
- final int result = array.getColor(0, DEFAULT_STROKE_COLOR);
- array.recycle();
- return result;
- }
-
/**
* A variant of {@link RectF#contains(float, float)} that also allows the point to reside on
* the right boundary of the rectangle.
diff --git a/core/java/android/widget/TextInputTimePickerView.java b/core/java/android/widget/TextInputTimePickerView.java
index 0cf8faa..e0261ad 100644
--- a/core/java/android/widget/TextInputTimePickerView.java
+++ b/core/java/android/widget/TextInputTimePickerView.java
@@ -174,7 +174,8 @@
*/
void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour,
boolean hourFormatStartsAtZero) {
- final String format = "%d";
+ final String hourFormat = "%d";
+ final String minuteFormat = "%02d";
mIs24Hour = is24Hour;
mHourFormatStartsAtZero = hourFormatStartsAtZero;
@@ -187,8 +188,8 @@
mAmPmSpinner.setSelection(1);
}
- mHourEditText.setText(String.format(format, localizedHour));
- mMinuteEditText.setText(String.format(format, minute));
+ mHourEditText.setText(String.format(hourFormat, localizedHour));
+ mMinuteEditText.setText(String.format(minuteFormat, minute));
if (mErrorShowing) {
validateInput();
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 78ca011..cb68bc3 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -27,8 +27,8 @@
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.media.update.ApiLoader;
-import android.media.update.FrameLayoutHelper;
import android.media.update.VideoView2Provider;
+import android.media.update.ViewGroupHelper;
import android.net.Uri;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -101,7 +101,7 @@
*
* @hide
*/
-public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> {
+public class VideoView2 extends ViewGroupHelper<VideoView2Provider> {
/** @hide */
@IntDef({
VIEW_TYPE_TEXTUREVIEW,
@@ -139,10 +139,12 @@
public VideoView2(
@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
- super((instance, superProvider) ->
+ super((instance, superProvider, privateProvider) ->
ApiLoader.getProvider(context).createVideoView2(
- (VideoView2) instance, superProvider, attrs, defStyleAttr, defStyleRes),
+ (VideoView2) instance, superProvider, privateProvider,
+ attrs, defStyleAttr, defStyleRes),
context, attrs, defStyleAttr, defStyleRes);
+ mProvider.initialize(attrs, defStyleAttr, defStyleRes);
}
/**
@@ -150,9 +152,10 @@
* instance if any.
*
* @param mediaControlView a media control view2 instance.
+ * @param intervalMs a time interval in milliseconds until VideoView2 hides MediaControlView2.
*/
- public void setMediaControlView2(MediaControlView2 mediaControlView) {
- mProvider.setMediaControlView2_impl(mediaControlView);
+ public void setMediaControlView2(MediaControlView2 mediaControlView, long intervalMs) {
+ mProvider.setMediaControlView2_impl(mediaControlView, intervalMs);
}
/**
@@ -179,11 +182,21 @@
/**
* Shows or hides closed caption or subtitles if there is any.
- * The first subtitle track will be chosen by default if there multiple subtitle tracks exist.
- * @param show shows closed caption or subtitles if this value is true, or hides.
+ * The first subtitle track will be chosen if there multiple subtitle tracks exist.
+ * Default behavior of VideoView2 is not showing subtitle.
+ * @param enable shows closed caption or subtitles if this value is true, or hides.
*/
- public void showSubtitle(boolean show) {
- mProvider.showSubtitle_impl(show);
+ public void setSubtitleEnabled(boolean enable) {
+ mProvider.setSubtitleEnabled_impl(enable);
+ }
+
+ /**
+ * Returns true if showing subtitle feature is enabled or returns false.
+ * Although there is no subtitle track or closed caption, it can return true, if the feature
+ * has been enabled by {@link #setSubtitleEnabled}.
+ */
+ public boolean isSubtitleEnabled() {
+ return mProvider.isSubtitleEnabled_impl();
}
/**
@@ -487,4 +500,9 @@
*/
void onCustomAction(String action, Bundle extras);
}
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mProvider.onLayout_impl(changed, l, t, r, b);
+ }
}
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index bb75bf6..fa8db96 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -31,7 +31,9 @@
private static final String TAG = "AlsaCardsParser";
protected static final boolean DEBUG = false;
- private static final String kCardsFilePath = "/proc/asound/cards";
+ private static final String kAlsaFolderPath = "/proc/asound";
+ private static final String kCardsFilePath = kAlsaFolderPath + "/cards";
+ private static final String kDeviceAddressPrefix = "/dev/bus/usb/";
private static LineTokenizer mTokenizer = new LineTokenizer(" :[]");
@@ -47,14 +49,31 @@
private static final String TAG = "AlsaCardRecord";
private static final String kUsbCardKeyStr = "at usb-";
- public int mCardNum = -1;
- public String mField1 = "";
- public String mCardName = "";
- public String mCardDescription = "";
- public boolean mIsUsb = false;
+ int mCardNum = -1;
+ String mField1 = "";
+ String mCardName = "";
+ String mCardDescription = "";
+
+ private String mUsbDeviceAddress = null;
public AlsaCardRecord() {}
+ public int getCardNum() {
+ return mCardNum;
+ }
+
+ public String getCardName() {
+ return mCardName;
+ }
+
+ public String getCardDescription() {
+ return mCardDescription;
+ }
+
+ public void setDeviceAddress(String usbDeviceAddress) {
+ mUsbDeviceAddress = usbDeviceAddress;
+ }
+
private boolean parse(String line, int lineIndex) {
int tokenIndex = 0;
int delimIndex = 0;
@@ -87,8 +106,8 @@
tokenIndex = mTokenizer.nextToken(line, 0);
if (tokenIndex != -1) {
int keyIndex = line.indexOf(kUsbCardKeyStr);
- mIsUsb = keyIndex != -1;
- if (mIsUsb) {
+ boolean isUsb = keyIndex != -1;
+ if (isUsb) {
mCardDescription = line.substring(tokenIndex, keyIndex - 1);
}
}
@@ -97,14 +116,18 @@
return true;
}
+ boolean isUsb() {
+ return mUsbDeviceAddress != null;
+ }
+
public String textFormat() {
- return mCardName + " : " + mCardDescription;
+ return mCardName + " : " + mCardDescription + " [addr:" + mUsbDeviceAddress + "]";
}
public void log(int listIndex) {
Slog.d(TAG, "" + listIndex +
" [" + mCardNum + " " + mCardName + " : " + mCardDescription +
- " usb:" + mIsUsb);
+ " usb:" + isUsb());
}
}
@@ -112,7 +135,7 @@
public int scan() {
if (DEBUG) {
- Slog.i(TAG, "AlsaCardsParser.scan()....");
+ Slog.d(TAG, "AlsaCardsParser.scan()....");
}
mCardRecords = new ArrayList<AlsaCardRecord>();
@@ -125,7 +148,7 @@
while ((line = bufferedReader.readLine()) != null) {
AlsaCardRecord cardRecord = new AlsaCardRecord();
if (DEBUG) {
- Slog.i(TAG, " " + line);
+ Slog.d(TAG, " " + line);
}
cardRecord.parse(line, 0);
@@ -134,10 +157,23 @@
break;
}
if (DEBUG) {
- Slog.i(TAG, " " + line);
+ Slog.d(TAG, " " + line);
}
cardRecord.parse(line, 1);
+ // scan "usbbus" file
+ int cardNum = cardRecord.mCardNum;
+ String cardFolderPath = kAlsaFolderPath + "/card" + cardNum;
+ File usbbusFile = new File(cardFolderPath + "/usbbus");
+ if (usbbusFile.exists()) {
+ // read it in
+ FileReader usbbusReader = new FileReader(usbbusFile);
+ String deviceAddress = (new BufferedReader(usbbusReader)).readLine();
+ if (deviceAddress != null) {
+ cardRecord.setDeviceAddress(kDeviceAddressPrefix + deviceAddress);
+ }
+ usbbusReader.close();
+ }
mCardRecords.add(cardRecord);
}
reader.close();
@@ -147,14 +183,12 @@
mScanStatus = SCANSTATUS_EMPTY;
}
} catch (FileNotFoundException e) {
- e.printStackTrace();
mScanStatus = SCANSTATUS_FAIL;
} catch (IOException e) {
- e.printStackTrace();
mScanStatus = SCANSTATUS_FAIL;
}
if (DEBUG) {
- Slog.i(TAG, " status:" + mScanStatus);
+ Slog.d(TAG, " status:" + mScanStatus);
}
return mScanStatus;
}
@@ -163,142 +197,33 @@
return mScanStatus;
}
- public ArrayList<AlsaCardRecord> getScanRecords() {
- return mCardRecords;
- }
-
- public AlsaCardRecord getCardRecordAt(int index) {
- return mCardRecords.get(index);
- }
-
- public AlsaCardRecord getCardRecordFor(int cardNum) {
- for (AlsaCardRecord rec : mCardRecords) {
- if (rec.mCardNum == cardNum) {
- return rec;
+ public AlsaCardRecord findCardNumFor(String deviceAddress) {
+ for(AlsaCardRecord cardRec : mCardRecords) {
+ if (cardRec.isUsb() && cardRec.mUsbDeviceAddress.equals(deviceAddress)) {
+ return cardRec;
}
}
-
return null;
}
- public int getNumCardRecords() {
- return mCardRecords.size();
- }
-
- public boolean isCardUsb(int cardNum) {
- for (AlsaCardRecord rec : mCardRecords) {
- if (rec.mCardNum == cardNum) {
- return rec.mIsUsb;
- }
- }
-
- return false;
- }
-
- // return -1 if none found
- public int getDefaultUsbCard() {
- // save the current list of devices
- ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords;
- if (DEBUG) {
- LogDevices("Previous Devices:", prevRecs);
- }
-
- // get the new list of devices
- if (scan() != SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error scanning Alsa cards file.");
- return -1;
- }
-
- if (DEBUG) {
- LogDevices("Current Devices:", mCardRecords);
- }
-
- // Calculate the difference between the old and new device list
- ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs);
- if (DEBUG) {
- LogDevices("New Devices:", newRecs);
- }
-
- // Choose the most-recently added EXTERNAL card
- // Check recently added devices
- for (AlsaCardRecord rec : newRecs) {
- if (DEBUG) {
- Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
- }
- if (rec.mIsUsb) {
- // Found it
- return rec.mCardNum;
- }
- }
-
- // or return the first added EXTERNAL card?
- for (AlsaCardRecord rec : prevRecs) {
- if (DEBUG) {
- Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
- }
- if (rec.mIsUsb) {
- return rec.mCardNum;
- }
- }
-
- return -1;
- }
-
- public int getDefaultCard() {
- // return an external card if possible
- int card = getDefaultUsbCard();
- if (DEBUG) {
- Slog.d(TAG, "getDefaultCard() default usb card:" + card);
- }
-
- if (card < 0 && getNumCardRecords() > 0) {
- // otherwise return the (internal) card with the highest number
- card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
- }
- if (DEBUG) {
- Slog.d(TAG, " returns card:" + card);
- }
- return card;
- }
-
- static public boolean hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum) {
- for (AlsaCardRecord cardRec : recs) {
- if (cardRec.mCardNum == cardNum) {
- return true;
- }
- }
- return false;
- }
-
- public ArrayList<AlsaCardRecord> getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs) {
- ArrayList<AlsaCardRecord> newRecs = new ArrayList<AlsaCardRecord>();
- for (AlsaCardRecord rec : mCardRecords) {
- // now scan to see if this card number is in the previous scan list
- if (!hasCardNumber(prevScanRecs, rec.mCardNum)) {
- newRecs.add(rec);
- }
- }
- return newRecs;
- }
-
//
// Logging
//
private void Log(String heading) {
if (DEBUG) {
- Slog.i(TAG, heading);
+ Slog.d(TAG, heading);
for (AlsaCardRecord cardRec : mCardRecords) {
- Slog.i(TAG, cardRec.textFormat());
+ Slog.d(TAG, cardRec.textFormat());
}
}
}
- static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
- Slog.d(TAG, caption + " ----------------");
- int listIndex = 0;
- for (AlsaCardRecord device : deviceList) {
- device.log(listIndex++);
- }
- Slog.d(TAG, "----------------");
- }
+// static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
+// Slog.d(TAG, caption + " ----------------");
+// int listIndex = 0;
+// for (AlsaCardRecord device : deviceList) {
+// device.log(listIndex++);
+// }
+// Slog.d(TAG, caption + "----------------");
+// }
}
diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
index 15261baf..8d92d01 100644
--- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -27,6 +27,9 @@
* @hide
* Retrieves information from an ALSA "devices" file.
*/
+/*
+ * NOTE: This class is currently not being used, but may be needed in the future.
+ */
public class AlsaDevicesParser {
private static final String TAG = "AlsaDevicesParser";
protected static final boolean DEBUG = false;
@@ -207,12 +210,6 @@
//
// Predicates
//
-/*
- public boolean hasPlaybackDevices() {
- return mHasPlaybackDevices;
- }
-*/
-
public boolean hasPlaybackDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -224,12 +221,6 @@
return false;
}
-/*
- public boolean hasCaptureDevices() {
- return mHasCaptureDevices;
- }
-*/
-
public boolean hasCaptureDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -241,12 +232,6 @@
return false;
}
-/*
- public boolean hasMIDIDevices() {
- return mHasMIDIDevices;
- }
-*/
-
public boolean hasMIDIDevices(int card) {
for (AlsaDeviceRecord deviceRecord : mDeviceRecords) {
if (deviceRecord.mCardNum == card &&
@@ -280,6 +265,7 @@
if (isLineDeviceRecord(line)) {
AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord();
deviceRecord.parse(line);
+ Slog.i(TAG, deviceRecord.textFormat());
mDeviceRecords.add(deviceRecord);
}
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index b22ce5e..f6a69d9 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 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.
@@ -16,157 +16,214 @@
package com.android.internal.app;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.annotation.Nullable;
+import android.animation.TimeAnimator;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
import android.os.Bundle;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.MathUtils;
-import android.view.Gravity;
-import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
-import android.widget.ImageView;
public class PlatLogoActivity extends Activity {
- public static final boolean FINISH = true;
+ FrameLayout layout;
+ TimeAnimator anim;
+ PBackground bg;
- FrameLayout mLayout;
- int mTapCount;
- int mKeyCount;
- PathInterpolator mInterpolator = new PathInterpolator(0f, 0f, 0.5f, 1f);
+ private class PBackground extends Drawable {
+ private float maxRadius, radius, x, y, dp;
+ private int[] palette;
+ private int darkest;
+ private float offset;
+
+ public PBackground() {
+ randomizePalette();
+ }
+
+ /**
+ * set inner radius of "p" logo
+ */
+ public void setRadius(float r) {
+ this.radius = Math.max(48*dp, r);
+ }
+
+ /**
+ * move the "p"
+ */
+ public void setPosition(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * for animating the "p"
+ */
+ public void setOffset(float o) {
+ this.offset = o;
+ }
+
+ /**
+ * rough luminance calculation
+ * https://www.w3.org/TR/AERT/#color-contrast
+ */
+ public float lum(int rgb) {
+ return ((Color.red(rgb) * 299f) + (Color.green(rgb) * 587f) + (Color.blue(rgb) * 114f)) / 1000f;
+ }
+
+ /**
+ * create a random evenly-spaced color palette
+ * guaranteed to contrast!
+ */
+ public void randomizePalette() {
+ final int slots = 2 + (int)(Math.random() * 2);
+ float[] color = new float[] { (float) Math.random() * 360f, 1f, 1f };
+ palette = new int[slots];
+ darkest = 0;
+ for (int i=0; i<slots; i++) {
+ palette[i] = Color.HSVToColor(color);
+ color[0] += 360f/slots;
+ if (lum(palette[i]) < lum(palette[darkest])) darkest = i;
+ }
+
+ final StringBuilder str = new StringBuilder();
+ for (int c : palette) {
+ str.append(String.format("#%08x ", c));
+ }
+ Log.v("PlatLogoActivity", "color palette: " + str);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (dp == 0) dp = getResources().getDisplayMetrics().density;
+ final float width = canvas.getWidth();
+ final float height = canvas.getHeight();
+ if (radius == 0) {
+ setPosition(width / 2, height / 2);
+ setRadius(width / 6);
+ }
+ final float inner_w = radius * 0.667f;
+
+ final Paint paint = new Paint();
+ paint.setStrokeCap(Paint.Cap.BUTT);
+ canvas.translate(x, y);
+
+ Path p = new Path();
+ p.moveTo(-radius, height);
+ p.lineTo(-radius, 0);
+ p.arcTo(-radius, -radius, radius, radius, -180, 270, false);
+ p.lineTo(-radius, radius);
+
+ float w = Math.max(canvas.getWidth(), canvas.getHeight()) * 1.414f;
+ paint.setStyle(Paint.Style.FILL);
+
+ int i=0;
+ while (w > radius*2 + inner_w*2) {
+ paint.setColor(0xFF000000 | palette[i % palette.length]);
+ // for a slower but more complete version:
+ // paint.setStrokeWidth(w);
+ // canvas.drawPath(p, paint);
+ canvas.drawOval(-w/2, -w/2, w/2, w/2, paint);
+ w -= inner_w * (1.1f + Math.sin((i/20f + offset) * 3.14159f));
+ i++;
+ }
+
+ // the innermost circle needs to be a constant color to avoid rapid flashing
+ paint.setColor(0xFF000000 | palette[(darkest+1) % palette.length]);
+ canvas.drawOval(-radius, -radius, radius, radius, paint);
+
+ p.reset();
+ p.moveTo(-radius, height);
+ p.lineTo(-radius, 0);
+ p.arcTo(-radius, -radius, radius, radius, -180, 270, false);
+ p.lineTo(-radius + inner_w, radius);
+
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(inner_w*2);
+ paint.setColor(palette[darkest]);
+ canvas.drawPath(p, paint);
+ paint.setStrokeWidth(inner_w);
+ paint.setColor(0xFFFFFFFF);
+ canvas.drawPath(p, paint);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mLayout = new FrameLayout(this);
- setContentView(mLayout);
+ layout = new FrameLayout(this);
+ setContentView(layout);
+
+ bg = new PBackground();
+ layout.setBackground(bg);
+
+ layout.setOnTouchListener(new View.OnTouchListener() {
+ final PointerCoords pc0 = new PointerCoords();
+ final PointerCoords pc1 = new PointerCoords();
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ if (event.getPointerCount() > 1) {
+ event.getPointerCoords(0, pc0);
+ event.getPointerCoords(1, pc1);
+ bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f);
+ }
+ break;
+ }
+ return true;
+ }
+ });
}
@Override
- public void onAttachedToWindow() {
- final DisplayMetrics dm = getResources().getDisplayMetrics();
- final float dp = dm.density;
- final int size = (int)
- (Math.min(Math.min(dm.widthPixels, dm.heightPixels), 600*dp) - 100*dp);
+ public void onStart() {
+ super.onStart();
- final ImageView im = new ImageView(this);
- final int pad = (int)(40*dp);
- im.setPadding(pad, pad, pad, pad);
- im.setTranslationZ(20);
- im.setScaleX(0.5f);
- im.setScaleY(0.5f);
- im.setAlpha(0f);
+ bg.randomizePalette();
- im.setBackground(new RippleDrawable(
- ColorStateList.valueOf(0xFF776677),
- getDrawable(com.android.internal.R.drawable.platlogo),
- null));
- im.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- final int w = view.getWidth();
- final int h = view.getHeight();
- outline.setOval((int)(w*.125), (int)(h*.125), (int)(w*.96), (int)(h*.96));
- }
- });
- im.setElevation(12f*dp);
- im.setClickable(true);
- im.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- im.setOnLongClickListener(new View.OnLongClickListener() {
+ anim = new TimeAnimator();
+ anim.setTimeListener(
+ new TimeAnimator.TimeListener() {
@Override
- public boolean onLongClick(View v) {
- if (mTapCount < 5) return false;
-
- final ContentResolver cr = getContentResolver();
- if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0)
- == 0) {
- // For posterity: the moment this user unlocked the easter egg
- try {
- Settings.System.putLong(cr,
- Settings.System.EGG_MODE,
- System.currentTimeMillis());
- } catch (RuntimeException e) {
- Log.e("PlatLogoActivity", "Can't write settings", e);
- }
- }
- im.post(new Runnable() {
- @Override
- public void run() {
- try {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- .addCategory("com.android.internal.category.PLATLOGO"));
- } catch (ActivityNotFoundException ex) {
- Log.e("PlatLogoActivity", "No more eggs.");
- }
- if (FINISH) finish();
- }
- });
- return true;
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ bg.setOffset((float) totalTime / 60000f);
+ bg.invalidateSelf();
}
});
- mTapCount++;
- }
- });
- // Enable hardware keyboard input for TV compatibility.
- im.setFocusable(true);
- im.requestFocus();
- im.setOnKeyListener(new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
- ++mKeyCount;
- if (mKeyCount > 2) {
- if (mTapCount > 5) {
- im.performLongClick();
- } else {
- im.performClick();
- }
- }
- return true;
- } else {
- return false;
- }
- }
- });
+ anim.start();
+ }
- mLayout.addView(im, new FrameLayout.LayoutParams(size, size, Gravity.CENTER));
-
- im.animate().scaleX(1f).scaleY(1f).alpha(1f)
- .setInterpolator(mInterpolator)
- .setDuration(500)
- .setStartDelay(800)
- .start();
+ @Override
+ public void onStop() {
+ if (anim != null) {
+ anim.cancel();
+ anim = null;
+ }
+ super.onStop();
}
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 1dfff5e..6bd6930 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -106,7 +106,7 @@
int flags = PackageManager.MATCH_DEFAULT_ONLY
| (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0);
- if (intent.isBrowsableWebIntent()
+ if (intent.isWebIntent()
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
flags |= PackageManager.MATCH_INSTANT;
}
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 902c8c1..6f7695c 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -21,24 +21,15 @@
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
import android.view.Window;
-import android.widget.TextView;
import com.android.internal.R;
@@ -85,14 +76,9 @@
return;
}
- View rootView = LayoutInflater.from(this).inflate(R.layout.unlaunchable_app_activity, null);
- TextView titleView = (TextView)rootView.findViewById(R.id.unlaunchable_app_title);
- TextView messageView = (TextView)rootView.findViewById(R.id.unlaunchable_app_message);
- titleView.setText(dialogTitle);
- messageView.setText(dialogMessage);
-
AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setView(rootView)
+ .setTitle(dialogTitle)
+ .setMessage(dialogMessage)
.setOnDismissListener(this);
if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
builder.setPositiveButton(R.string.work_mode_turn_on, this)
diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java
index 9b7383f..7b25a06 100644
--- a/core/java/com/android/internal/colorextraction/types/Tonal.java
+++ b/core/java/com/android/internal/colorextraction/types/Tonal.java
@@ -405,12 +405,13 @@
return v - (float) Math.floor(v);
}
- static class TonalPalette {
- final float[] h;
- final float[] s;
- final float[] l;
- final float minHue;
- final float maxHue;
+ @VisibleForTesting
+ public static class TonalPalette {
+ public final float[] h;
+ public final float[] s;
+ public final float[] l;
+ public final float minHue;
+ public final float maxHue;
TonalPalette(float[] h, float[] s, float[] l) {
if (h.length != s.length || s.length != l.length) {
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index 10d44bd..ef44ef7 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
jsharkey@android.com
lorenzo@google.com
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7def876..eb58b09 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -678,7 +678,8 @@
StopwatchTimer mCameraOnTimer;
int mGpsSignalQualityBin = -1;
- final StopwatchTimer[] mGpsSignalQualityTimer =
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
@@ -760,6 +761,8 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
+ boolean mIsCellularTxPowerHigh = false;
+
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
@@ -3522,7 +3525,7 @@
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
- void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
@@ -3603,8 +3606,8 @@
} else if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
return;
}
@@ -3642,7 +3645,7 @@
return;
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
return;
}
@@ -3650,15 +3653,14 @@
// The history is currently empty; we need it to start with a time stamp.
cur.currentTime = System.currentTimeMillis();
if (recordResetDueToOverflow) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
- private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
- HistoryItem cur) {
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
if (mIteratingHistory) {
throw new IllegalStateException("Can't do this while iterating history!");
}
@@ -3692,17 +3694,17 @@
mHistoryAddTmp.wakeReasonTag = null;
mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
- addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+ addHistoryRecordInnerLocked(wakeElapsedTime, mHistoryAddTmp);
}
}
mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
mTrackRunningHistoryUptime = uptimeMs;
- addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ addHistoryRecordInnerLocked(elapsedRealtimeMs, mHistoryCur);
}
- void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
+ void addHistoryRecordInnerLocked(long elapsedRealtimeMs, HistoryItem cur) {
+ addHistoryBufferLocked(elapsedRealtimeMs, cur);
if (!USE_OLD_HISTORY) {
return;
@@ -3743,7 +3745,7 @@
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -3760,7 +3762,7 @@
}
}
- addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
@@ -3826,6 +3828,7 @@
mActiveHistoryStates2 = 0xffffffff;
}
+ @GuardedBy("this")
public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
final boolean screenOff = !isScreenOn(screenState);
@@ -3880,7 +3883,8 @@
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
final Uid u = getUidStatsLocked(appUid);
u.addIsolatedUid(isolatedUid);
}
@@ -3902,9 +3906,11 @@
* This should only be called after the cpu times have been read.
* @see #scheduleRemoveIsolatedUidLocked(int, int)
*/
+ @GuardedBy("this")
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
- StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+ StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
+ isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
@@ -4255,10 +4261,12 @@
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@@ -4298,10 +4306,12 @@
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
}
}
@@ -4426,7 +4436,8 @@
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 1);
+ uid, null, name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4439,7 +4450,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 1);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4450,7 +4462,8 @@
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 1);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -4470,8 +4483,8 @@
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null,
+ name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4484,7 +4497,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 0);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4494,7 +4508,8 @@
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 0);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -4518,7 +4533,8 @@
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
- StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
+ StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+ /* duration_usec */ deltaUptime * 1000);
mLastWakeupReason = null;
}
}
@@ -4659,10 +4675,12 @@
mGpsNesting++;
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
}
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -4683,10 +4701,11 @@
}
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 0);
+ workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
@@ -4717,6 +4736,7 @@
return;
}
+ @GuardedBy("this")
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
@@ -4941,7 +4961,9 @@
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
- StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
+ StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -5545,16 +5567,19 @@
if (workChain != null) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 1);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
}
@@ -5594,16 +5619,19 @@
if (workChain != null) {
StatsLog.write(
- StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
+ StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 0);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
}
@@ -5656,7 +5684,8 @@
for (int j = 0; j < allWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
allWorkChains.get(j).getUids(),
- allWorkChains.get(j).getTags(), 0);
+ allWorkChains.get(j).getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
}
allWorkChains.clear();
}
@@ -5666,7 +5695,8 @@
for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
unoptimizedWorkChains.get(j).getUids(),
- unoptimizedWorkChains.get(j).getTags(), 0);
+ unoptimizedWorkChains.get(j).getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
unoptimizedWorkChains.clear();
}
@@ -6011,7 +6041,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6021,7 +6052,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6031,7 +6063,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6041,7 +6074,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -6052,7 +6086,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 1);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6062,7 +6096,7 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 1);
+ workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6073,7 +6107,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 0);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6083,7 +6117,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7035,7 +7070,8 @@
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
}
@@ -7045,7 +7081,8 @@
mWifiMulticastEnabled = false;
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
}
}
@@ -7098,14 +7135,16 @@
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7113,7 +7152,8 @@
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
@@ -7127,7 +7167,8 @@
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7135,7 +7176,7 @@
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
- null, 0);
+ null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
}
@@ -7144,7 +7185,7 @@
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
- 0);
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
@@ -7158,7 +7199,8 @@
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7166,7 +7208,7 @@
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
- 0);
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7174,7 +7216,8 @@
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
@@ -7188,14 +7231,16 @@
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7203,7 +7248,8 @@
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
@@ -9607,6 +9653,7 @@
return ps;
}
+ @GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState) {
int uidRunningState;
// Make special note of Foreground Services
@@ -9777,7 +9824,8 @@
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__ON);
}
}
@@ -9786,7 +9834,8 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -9796,7 +9845,7 @@
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 1);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED);
}
}
@@ -9806,7 +9855,7 @@
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 0);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9888,8 +9937,6 @@
if (wl != null) {
StopwatchTimer wlt = getWakelockTimerLocked(wl, type);
wlt.stopRunningLocked(elapsedRealtimeMs);
- if (!wlt.isRunningLocked()) { // only tell statsd if truly stopped
- }
}
if (type == WAKE_TYPE_PARTIAL) {
if (mAggregatedPartialWakelockTimer != null) {
@@ -9919,7 +9966,7 @@
t.startRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
- 1);
+ StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
}
}
@@ -9930,7 +9977,7 @@
t.stopRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
- sensor, 0);
+ sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -11171,6 +11218,9 @@
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
+ // Add modem tx power to history.
+ addModemTxPowerToHistory(activityInfo);
+
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
@@ -11345,6 +11395,44 @@
new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
/**
+ * Add modem tx power to history
+ * Device is said to be in high cellular transmit power when it has spent most of the transmit
+ * time at the highest power level.
+ * @param activityInfo
+ */
+ private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+ if (activityInfo == null) {
+ return;
+ }
+ int[] txTimeMs = activityInfo.getTxTimeMillis();
+ if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) {
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ int levelMaxTimeSpent = 0;
+ for (int i = 1; i < txTimeMs.length; i++) {
+ if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) {
+ levelMaxTimeSpent = i;
+ }
+ }
+ if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (!mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = true;
+ }
+ return;
+ }
+ if (mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = false;
+ }
+ return;
+ }
+
+ /**
* Distribute Bluetooth energy info and network traffic to apps.
* @param info The energy information from the bluetooth controller.
*/
@@ -11678,6 +11766,7 @@
* and we are on battery with screen off, we give more of the cpu time to those apps holding
* wakelocks. If the screen is on, we just assign the actual cpu time an app used.
*/
+ @GuardedBy("this")
public void updateCpuTimeLocked() {
if (mPowerProfile == null) {
return;
@@ -12123,6 +12212,7 @@
return false;
}
+ @GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
boolean doWrite = false;
@@ -12264,7 +12354,7 @@
boolean reset) {
mRecordingHistory = true;
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+ addHistoryBufferLocked(elapsedRealtimeMs,
reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
mHistoryCur.currentTime = 0;
@@ -12277,8 +12367,7 @@
final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = currentTime;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
- mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@@ -12286,8 +12375,7 @@
private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN,
- mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@@ -12301,6 +12389,7 @@
// This should probably be exposed in the API, though it's not critical
public static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
+ @GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int volt, final int chargeUAh,
final int chargeFullUAh) {
@@ -13116,6 +13205,7 @@
}
}
+ @GuardedBy("this")
public void dumpConstantsLocked(PrintWriter pw) {
mConstants.dumpLocked(pw);
}
@@ -13231,7 +13321,7 @@
if (USE_OLD_HISTORY) {
addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
}
- addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtime, HistoryItem.CMD_START, mHistoryCur);
startRecordingHistory(elapsedRealtime, uptime, false);
}
@@ -13499,6 +13589,7 @@
mCameraOnTimer.readSummaryFromParcelLocked(in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
+ mIsCellularTxPowerHigh = false;
int NRPMS = in.readInt();
if (NRPMS > 10000) {
@@ -14435,6 +14526,7 @@
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in);
+ mIsCellularTxPowerHigh = false;
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 088e726..12405eb 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -283,6 +283,7 @@
return -OsConstants.EBADF;
}
+ @GuardedBy("mLock")
private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
if (entry == null) {
@@ -291,12 +292,14 @@
return entry;
}
+ @GuardedBy("mLock")
private void recycleLocked(Args args) {
if (mArgsPool.size() < ARGS_POOL_SIZE) {
mArgsPool.add(args);
}
}
+ @GuardedBy("mLock")
private void replySimpleLocked(long unique, int result) {
if (mInstance != 0) {
native_replySimple(mInstance, unique, result);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index f9a2341..e69a360 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
/** Do not enfore hidden API access restrictions. */
public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 39279b5..74802c8 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -583,7 +583,7 @@
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null);
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index 245a66e..1376687 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -96,4 +96,14 @@
context.getApplicationInfo().className,
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 logAppOverlayExit(int uid, String packageName, boolean usingAlertWindow) {
+ StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, usingAlertWindow,
+ 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 34b5ec8..03a7cd2 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3158,7 +3158,7 @@
Bundle args = new Bundle();
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId());
return ((SearchManager)getContext().getSystemService(Context.SEARCH_SERVICE))
- .launchLegacyAssist(null, UserHandle.myUserId(), args);
+ .launchLegacyAssist(null, getContext().getUserId(), args);
}
return result;
}
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 3192d5c..1916c11 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -16,10 +16,9 @@
package com.android.internal.print;
+import static com.android.internal.util.dump.DumpUtils.writeComponentName;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.ComponentNameProto;
import android.content.Context;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -40,42 +39,13 @@
import android.service.print.PrinterInfoProto;
import android.service.print.ResolutionProto;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
/**
* Utilities for dumping print related proto buffer
*/
public class DumpUtils {
/**
- * Write a string to a proto if the string is not {@code null}.
- *
- * @param proto The proto to write to
- * @param idName Clear text name of the proto-id
- * @param id The proto-id of the string
- * @param string The string to write
- */
- public static void writeStringIfNotNull(@NonNull DualDumpOutputStream proto, String idName,
- long id, @Nullable String string) {
- if (string != null) {
- proto.write(idName, id, string);
- }
- }
-
- /**
- * Write a {@link ComponentName} to a proto.
- *
- * @param proto The proto to write to
- * @param idName Clear text name of the proto-id
- * @param id The proto-id of the component name
- * @param component The component name to write
- */
- public static void writeComponentName(@NonNull DualDumpOutputStream proto, String idName,
- long id, @NonNull ComponentName component) {
- long token = proto.start(idName, id);
- proto.write("package_name", ComponentNameProto.PACKAGE_NAME, component.getPackageName());
- proto.write("class_name", ComponentNameProto.CLASS_NAME, component.getClassName());
- proto.end(token);
- }
-
- /**
* Write a {@link PrinterId} to a proto.
*
* @param proto The proto to write to
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f70d3c2..221bf88 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -35,7 +35,7 @@
void animateCollapsePanels();
void togglePanel();
- void showChargingAnimation(int batteryLevel);
+ void showWirelessChargingAnimation(int batteryLevel);
/**
* Notifies the status bar of a System UI visibility flag change.
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
new file mode 100644
index 0000000..cac2265
--- /dev/null
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -0,0 +1,237 @@
+/*
+ * 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.usb;
+
+import static android.hardware.usb.UsbPort.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPort.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPort.MODE_DFP;
+import static android.hardware.usb.UsbPort.MODE_DUAL;
+import static android.hardware.usb.UsbPort.MODE_NONE;
+import static android.hardware.usb.UsbPort.MODE_UFP;
+
+import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
+
+import android.annotation.NonNull;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbConfiguration;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.V1_0.Constants;
+import android.service.usb.UsbAccessoryProto;
+import android.service.usb.UsbConfigurationProto;
+import android.service.usb.UsbDeviceProto;
+import android.service.usb.UsbEndPointProto;
+import android.service.usb.UsbInterfaceProto;
+import android.service.usb.UsbPortProto;
+import android.service.usb.UsbPortStatusProto;
+import android.service.usb.UsbPortStatusRoleCombinationProto;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+/** Dump methods for public USB classes */
+public class DumpUtils {
+ public static void writeAccessory(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, @NonNull UsbAccessory accessory) {
+ long token = dump.start(idName, id);
+
+ dump.write("manufacturer", UsbAccessoryProto.MANUFACTURER, accessory.getManufacturer());
+ dump.write("model", UsbAccessoryProto.MODEL, accessory.getModel());
+ writeStringIfNotNull(dump, "description", UsbAccessoryProto.DESCRIPTION,
+ accessory.getManufacturer());
+ dump.write("version", UsbAccessoryProto.VERSION, accessory.getVersion());
+ writeStringIfNotNull(dump, "uri", UsbAccessoryProto.URI, accessory.getUri());
+ dump.write("serial", UsbAccessoryProto.SERIAL, accessory.getSerial());
+
+ dump.end(token);
+ }
+
+ public static void writeDevice(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, @NonNull UsbDevice device) {
+ long token = dump.start(idName, id);
+
+ dump.write("name", UsbDeviceProto.NAME, device.getDeviceName());
+ dump.write("vendor_id", UsbDeviceProto.VENDOR_ID, device.getVendorId());
+ dump.write("product_id", UsbDeviceProto.PRODUCT_ID, device.getProductId());
+ dump.write("class", UsbDeviceProto.CLASS, device.getDeviceClass());
+ dump.write("subclass", UsbDeviceProto.SUBCLASS, device.getDeviceSubclass());
+ dump.write("protocol", UsbDeviceProto.PROTOCOL, device.getDeviceProtocol());
+ dump.write("manufacturer_name", UsbDeviceProto.MANUFACTURER_NAME,
+ device.getManufacturerName());
+ dump.write("product_name", UsbDeviceProto.PRODUCT_NAME, device.getProductName());
+ dump.write("version", UsbDeviceProto.VERSION, device.getVersion());
+ dump.write("serial_number", UsbDeviceProto.SERIAL_NUMBER, device.getSerialNumber());
+
+ int numConfigurations = device.getConfigurationCount();
+ for (int i = 0; i < numConfigurations; i++) {
+ writeConfiguration(dump, "configurations", UsbDeviceProto.CONFIGURATIONS,
+ device.getConfiguration(i));
+ }
+
+ dump.end(token);
+ }
+
+ private static void writeConfiguration(@NonNull DualDumpOutputStream dump,
+ @NonNull String idName, long id, @NonNull UsbConfiguration configuration) {
+ long token = dump.start(idName, id);
+ dump.write("id", UsbConfigurationProto.ID, configuration.getId());
+ dump.write("name", UsbConfigurationProto.NAME, configuration.getName());
+ dump.write("attributes", UsbConfigurationProto.ATTRIBUTES, configuration.getAttributes());
+ dump.write("max_power", UsbConfigurationProto.MAX_POWER, configuration.getMaxPower());
+
+ int numInterfaces = configuration.getInterfaceCount();
+ for (int i = 0; i < numInterfaces; i++) {
+ writeInterface(dump, "interfaces", UsbConfigurationProto.INTERFACES,
+ configuration.getInterface(i));
+ }
+ dump.end(token);
+ }
+
+ private static void writeInterface(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, @NonNull UsbInterface iface) {
+ long token = dump.start(idName, id);
+
+ dump.write("id", UsbInterfaceProto.ID, iface.getId());
+ dump.write("alternate_settings", UsbInterfaceProto.ALTERNATE_SETTINGS,
+ iface.getAlternateSetting());
+ dump.write("name", UsbInterfaceProto.NAME, iface.getName());
+ dump.write("class", UsbInterfaceProto.CLASS, iface.getInterfaceClass());
+ dump.write("subclass", UsbInterfaceProto.SUBCLASS, iface.getInterfaceSubclass());
+ dump.write("protocol", UsbInterfaceProto.PROTOCOL, iface.getInterfaceProtocol());
+
+ int numEndpoints = iface.getEndpointCount();
+ for (int i = 0; i < numEndpoints; i++) {
+ writeEndpoint(dump, "endpoints", UsbInterfaceProto.ENDPOINTS, iface.getEndpoint(i));
+ }
+ dump.end(token);
+ }
+
+ private static void writeEndpoint(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, @NonNull UsbEndpoint endpoint) {
+ long token = dump.start(idName, id);
+
+ dump.write("endpoint_number", UsbEndPointProto.ENDPOINT_NUMBER,
+ endpoint.getEndpointNumber());
+ dump.write("direction", UsbEndPointProto.DIRECTION, endpoint.getDirection());
+ dump.write("address", UsbEndPointProto.ADDRESS, endpoint.getAddress());
+ dump.write("type", UsbEndPointProto.TYPE, endpoint.getType());
+ dump.write("attributes", UsbEndPointProto.ATTRIBUTES,
+ endpoint.getAttributes());
+ dump.write("max_packet_size", UsbEndPointProto.MAX_PACKET_SIZE,
+ endpoint.getMaxPacketSize());
+ dump.write("interval", UsbEndPointProto.INTERVAL, endpoint.getInterval());
+
+ dump.end(token);
+ }
+
+ public static void writePort(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, @NonNull UsbPort port) {
+ long token = dump.start(idName, id);
+
+ dump.write("id", UsbPortProto.ID, port.getId());
+
+ int mode = port.getSupportedModes();
+ if (dump.isProto()) {
+ if (mode == MODE_NONE) {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES, MODE_NONE);
+ } else {
+ if ((mode & MODE_DUAL) == MODE_DUAL) {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES, MODE_DUAL);
+ } else {
+ if ((mode & MODE_DFP) == MODE_DFP) {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES, MODE_DFP);
+ } else if ((mode & MODE_UFP) == MODE_UFP) {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES, MODE_UFP);
+ }
+ }
+
+ if ((mode & MODE_AUDIO_ACCESSORY) == MODE_AUDIO_ACCESSORY) {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES,
+ MODE_AUDIO_ACCESSORY);
+ }
+
+ if ((mode & MODE_DEBUG_ACCESSORY) == MODE_DEBUG_ACCESSORY) {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES,
+ MODE_DEBUG_ACCESSORY);
+ }
+ }
+ } else {
+ dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES, UsbPort.modeToString(mode));
+ }
+
+ dump.end(token);
+ }
+
+ private static void writePowerRole(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, int powerRole) {
+ if (dump.isProto()) {
+ dump.write(idName, id, powerRole);
+ } else {
+ dump.write(idName, id, UsbPort.powerRoleToString(powerRole));
+ }
+ }
+
+ private static void writeDataRole(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, int dataRole) {
+ if (dump.isProto()) {
+ dump.write(idName, id, dataRole);
+ } else {
+ dump.write(idName, id, UsbPort.dataRoleToString(dataRole));
+ }
+ }
+
+
+ public static void writePortStatus(@NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id, @NonNull UsbPortStatus status) {
+ long token = dump.start(idName, id);
+
+ dump.write("connected", UsbPortStatusProto.CONNECTED, status.isConnected());
+
+ if (dump.isProto()) {
+ dump.write("current_mode", UsbPortStatusProto.CURRENT_MODE, status.getCurrentMode());
+ } else {
+ dump.write("current_mode", UsbPortStatusProto.CURRENT_MODE,
+ UsbPort.modeToString(status.getCurrentMode()));
+ }
+
+ writePowerRole(dump, "power_role", UsbPortStatusProto.POWER_ROLE,
+ status.getCurrentPowerRole());
+ writeDataRole(dump, "data_role", UsbPortStatusProto.DATA_ROLE, status.getCurrentDataRole());
+
+ int undumpedCombinations = status.getSupportedRoleCombinations();
+ while (undumpedCombinations != 0) {
+ int index = Integer.numberOfTrailingZeros(undumpedCombinations);
+ undumpedCombinations &= ~(1 << index);
+
+ int powerRole = (index / Constants.PortDataRole.NUM_DATA_ROLES
+ + Constants.PortPowerRole.NONE);
+ int dataRole = index % Constants.PortDataRole.NUM_DATA_ROLES;
+
+ long roleCombinationToken = dump.start("role_combinations",
+ UsbPortStatusProto.ROLE_COMBINATIONS);
+ writePowerRole(dump, "power_role", UsbPortStatusRoleCombinationProto.POWER_ROLE,
+ powerRole);
+ writeDataRole(dump, "data_role", UsbPortStatusRoleCombinationProto.DATA_ROLE,
+ dataRole);
+ dump.end(roleCombinationToken);
+ }
+
+ dump.end(token);
+ }
+}
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 71550be..f8885a2 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -160,7 +160,7 @@
final File file = new File(mBasePath, name);
final FileInputStream is = new FileInputStream(file);
try {
- Streams.copy(is, zos);
+ FileUtils.copy(is, zos);
} finally {
IoUtils.closeQuietly(is);
}
diff --git a/core/java/com/android/internal/print/DualDumpOutputStream.java b/core/java/com/android/internal/util/dump/DualDumpOutputStream.java
similarity index 92%
rename from core/java/com/android/internal/print/DualDumpOutputStream.java
rename to core/java/com/android/internal/util/dump/DualDumpOutputStream.java
index 4b10ef2..3ac38ed 100644
--- a/core/java/com/android/internal/print/DualDumpOutputStream.java
+++ b/core/java/com/android/internal/util/dump/DualDumpOutputStream.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.print;
+package com.android.internal.util.dump;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -131,27 +131,27 @@
}
}
+ /**
+ * Create a new DualDumpOutputStream.
+ *
+ * @param proto the {@link ProtoOutputStream}
+ */
+ public DualDumpOutputStream(@NonNull ProtoOutputStream proto) {
+ mProtoStream = proto;
+ mIpw = null;
+ }
/**
- * Create a new DualDumpOutputStream. Only one output should be set.
+ * Create a new DualDumpOutputStream.
*
- * @param proto If dumping to proto the {@link ProtoOutputStream}
- * @param ipw If dumping to a print writer, the {@link IndentingPrintWriter}
+ * @param ipw the {@link IndentingPrintWriter}
*/
- public DualDumpOutputStream(@Nullable ProtoOutputStream proto,
- @Nullable IndentingPrintWriter ipw) {
- if ((proto == null) == (ipw == null)) {
- Log.e(LOG_TAG, "Cannot dump to clear text and proto at once. Ignoring proto");
- proto = null;
- }
-
- mProtoStream = proto;
+ public DualDumpOutputStream(@NonNull IndentingPrintWriter ipw) {
+ mProtoStream = null;
mIpw = ipw;
- if (!isProto()) {
- // Add root object
- mDumpObjects.add(new DumpObject(null));
- }
+ // Add root object
+ mDumpObjects.add(new DumpObject(null));
}
public void write(@NonNull String fieldName, long fieldId, double val) {
diff --git a/core/java/com/android/internal/util/dump/DumpUtils.java b/core/java/com/android/internal/util/dump/DumpUtils.java
new file mode 100644
index 0000000..0ee204e
--- /dev/null
+++ b/core/java/com/android/internal/util/dump/DumpUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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.util.dump;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.ComponentNameProto;
+
+public class DumpUtils {
+ /**
+ * Write a string to a proto if the string is not {@code null}.
+ *
+ * @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
+ * @param id The proto-id of the string
+ * @param string The string to write
+ */
+ public static void writeStringIfNotNull(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @Nullable String string) {
+ if (string != null) {
+ proto.write(idName, id, string);
+ }
+ }
+
+ /**
+ * Write a {@link ComponentName} to a proto.
+ *
+ * @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
+ * @param id The proto-id of the component name
+ * @param component The component name to write
+ */
+ public static void writeComponentName(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull ComponentName component) {
+ long token = proto.start(idName, id);
+ proto.write("package_name", ComponentNameProto.PACKAGE_NAME, component.getPackageName());
+ proto.write("class_name", ComponentNameProto.CLASS_NAME, component.getClassName());
+ proto.end(token);
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5e0a986b..0282286 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -68,6 +68,7 @@
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
+ boolean isInputMethodPickerShownForTest();
void setInputMethod(in IBinder token, String id);
void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
void hideMySoftInput(in IBinder token, int flags);
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 74dbaba..f05bd32 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -139,6 +139,10 @@
* @see com.android.server.wm.WindowManagerService#inputMethodClientHasFocus(IInputMethodClient)
*/
int ERROR_NOT_IME_TARGET_WINDOW = 11;
+ /**
+ * Indicates that focused view in the current window is not an editor.
+ */
+ int ERROR_NO_EDITOR = 12;
}
@ResultCode
@@ -258,6 +262,8 @@
return "ERROR_NULL";
case ResultCode.ERROR_NO_IME:
return "ERROR_NO_IME";
+ case ResultCode.ERROR_NO_EDITOR:
+ return "ERROR_NO_EDITOR";
case ResultCode.ERROR_INVALID_PACKAGE_NAME:
return "ERROR_INVALID_PACKAGE_NAME";
case ResultCode.ERROR_SYSTEM_NOT_READY:
@@ -288,6 +294,10 @@
*/
public static final InputBindResult NO_IME = error(ResultCode.ERROR_NO_IME);
/**
+ * Predefined error object for {@link ResultCode#NO_EDITOR}.
+ */
+ public static final InputBindResult NO_EDITOR = error(ResultCode.ERROR_NO_EDITOR);
+ /**
* Predefined error object for {@link ResultCode#ERROR_INVALID_PACKAGE_NAME}.
*/
public static final InputBindResult INVALID_PACKAGE_NAME =
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index b53459e..67dc81a 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -551,6 +551,7 @@
mPreventDispatchingItemsChanged = true;
clear();
clearHeader();
+ mPresenters.clear();
mPreventDispatchingItemsChanged = false;
mItemsChangedWhileDispatchPrevented = false;
onItemsChanged(true);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 5673814..732534c 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -66,6 +66,8 @@
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
KeyChainSnapshot getKeyChainSnapshot();
byte[] generateAndStoreKey(String alias);
+ String generateKey(String alias, in byte[] account);
+ String getKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
Map getRecoverySnapshotVersions();
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
new file mode 100644
index 0000000..71d3bb5
--- /dev/null
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -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
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A class to extract Bitmaps from a MessagingStyle message.
+ */
+public class LocalImageResolver {
+
+ private static final int MAX_SAFE_ICON_SIZE_PX = 480;
+
+ @Nullable
+ public static Drawable resolveImage(Uri uri, Context context) throws IOException {
+ BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context);
+ if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
+ return null;
+ }
+
+ int originalSize =
+ (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth)
+ ? onlyBoundsOptions.outHeight
+ : onlyBoundsOptions.outWidth;
+
+ double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
+ ? (originalSize / MAX_SAFE_ICON_SIZE_PX)
+ : 1.0;
+
+ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
+ bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
+ InputStream input = context.getContentResolver().openInputStream(uri);
+ Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
+ input.close();
+ return new BitmapDrawable(context.getResources(), bitmap);
+ }
+
+ private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context)
+ throws IOException {
+ InputStream input = context.getContentResolver().openInputStream(uri);
+ BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
+ onlyBoundsOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
+ input.close();
+ return onlyBoundsOptions;
+ }
+
+ private static int getPowerOfTwoForSampleRatio(double ratio) {
+ int k = Integer.highestOneBit((int) Math.floor(ratio));
+ return Math.max(1, k);
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 5577d6e..239beaa 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -22,9 +22,12 @@
import android.annotation.StyleRes;
import android.app.Notification;
import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Pools;
import android.view.LayoutInflater;
import android.view.View;
@@ -61,6 +64,11 @@
private boolean mIsHidingAnimated;
private boolean mNeedsGeneratedAvatar;
private Notification.Person mSender;
+ private boolean mAvatarsAtEnd;
+ private ViewGroup mImageContainer;
+ private MessagingImageMessage mIsolatedMessage;
+ private boolean mTransformingImages;
+ private Point mDisplaySize = new Point();
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -87,6 +95,35 @@
mSenderName = findViewById(R.id.message_name);
mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
mAvatarView = findViewById(R.id.message_icon);
+ mImageContainer = findViewById(R.id.messaging_group_icon_container);
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ mDisplaySize.x = displayMetrics.widthPixels;
+ mDisplaySize.y = displayMetrics.heightPixels;
+ }
+
+ public void updateClipRect() {
+ // We want to clip to the senderName if it's available, otherwise our images will come
+ // from a weird position
+ Rect clipRect;
+ if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) {
+ ViewGroup parent = (ViewGroup) mSenderName.getParent();
+ int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent(
+ mMessageContainer, parent) + mSenderName.getHeight();
+ clipRect = new Rect(0, top, mDisplaySize.x, mDisplaySize.y);
+ } else {
+ clipRect = null;
+ }
+ mMessageContainer.setClipBounds(clipRect);
+ }
+
+ private int getDistanceFromParent(View searchedView, ViewGroup parent) {
+ int position = 0;
+ View view = searchedView;
+ while(view != parent) {
+ position += view.getTop() + view.getTranslationY();
+ view = (View) view.getParent();
+ }
+ return position;
}
public void setSender(Notification.Person sender, CharSequence nameOverride) {
@@ -129,12 +166,14 @@
}
public void removeMessage(MessagingMessage messagingMessage) {
- mMessageContainer.removeView(messagingMessage);
+ ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent();
+ messageParent.removeView(messagingMessage.getView());
Runnable recycleRunnable = () -> {
- mMessageContainer.removeTransientView(messagingMessage);
+ messageParent.removeTransientView(messagingMessage.getView());
messagingMessage.recycle();
if (mMessageContainer.getChildCount() == 0
- && mMessageContainer.getTransientViewCount() == 0) {
+ && mMessageContainer.getTransientViewCount() == 0
+ && mImageContainer.getChildCount() == 0) {
ViewParent parent = getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(MessagingGroup.this);
@@ -148,9 +187,10 @@
}
};
if (isShown()) {
- mMessageContainer.addTransientView(messagingMessage, 0);
- performRemoveAnimation(messagingMessage, recycleRunnable);
- if (mMessageContainer.getChildCount() == 0) {
+ messageParent.addTransientView(messagingMessage.getView(), 0);
+ performRemoveAnimation(messagingMessage.getView(), recycleRunnable);
+ if (mMessageContainer.getChildCount() == 0
+ && mImageContainer.getChildCount() == 0) {
removeGroupAnimated(null);
}
} else {
@@ -160,12 +200,8 @@
}
private void removeGroupAnimated(Runnable endAction) {
- MessagingPropertyAnimator.fadeOut(mAvatarView, null);
- MessagingPropertyAnimator.startLocalTranslationTo(mAvatarView,
- (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN);
- MessagingPropertyAnimator.fadeOut(mSenderName, null);
- MessagingPropertyAnimator.startLocalTranslationTo(mSenderName,
- (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN);
+ performRemoveAnimation(mAvatarView, null);
+ performRemoveAnimation(mSenderName, null);
boolean endActionTriggered = false;
for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) {
View child = mMessageContainer.getChildAt(i);
@@ -182,14 +218,17 @@
performRemoveAnimation(child, childEndAction);
endActionTriggered = true;
}
+ if (mIsolatedMessage != null) {
+ performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null);
+ endActionTriggered = true;
+ }
if (!endActionTriggered && endAction != null) {
endAction.run();
}
}
- public void performRemoveAnimation(View message,
- Runnable recycleRunnable) {
- MessagingPropertyAnimator.fadeOut(message, recycleRunnable);
+ public void performRemoveAnimation(View message, Runnable endAction) {
+ MessagingPropertyAnimator.fadeOut(message, endAction);
MessagingPropertyAnimator.startLocalTranslationTo(message,
(int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN);
}
@@ -222,6 +261,9 @@
}
}
}
+ if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) {
+ return mIsolatedMessage.getMeasuredType();
+ }
return MEASURED_NORMAL;
}
@@ -234,6 +276,7 @@
result += ((MessagingLinearLayout.MessagingChild) child).getConsumedLines();
}
}
+ result = mIsolatedMessage != null ? Math.max(result, 1) : result;
// A group is usually taking up quite some space with the padding and the name, let's add 1
return result + 1;
}
@@ -289,26 +332,67 @@
public void setMessages(List<MessagingMessage> group) {
// Let's now make sure all children are added and in the correct order
+ int textMessageIndex = 0;
+ MessagingImageMessage isolatedMessage = null;
for (int messageIndex = 0; messageIndex < group.size(); messageIndex++) {
MessagingMessage message = group.get(messageIndex);
+ message.setColor(mTextColor);
if (message.getGroup() != this) {
message.setMessagingGroup(this);
- ViewParent parent = mMessageContainer.getParent();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).removeView(message);
- }
- mMessageContainer.addView(message, messageIndex);
mAddedMessages.add(message);
}
- if (messageIndex != mMessageContainer.indexOfChild(message)) {
- mMessageContainer.removeView(message);
- mMessageContainer.addView(message, messageIndex);
+ boolean isImage = message instanceof MessagingImageMessage;
+ if (mAvatarsAtEnd && isImage) {
+ isolatedMessage = (MessagingImageMessage) message;
+ } else {
+ if (removeFromParentIfDifferent(message, mMessageContainer)) {
+ ViewGroup.LayoutParams layoutParams = message.getView().getLayoutParams();
+ if (layoutParams != null
+ && !(layoutParams instanceof MessagingLinearLayout.LayoutParams)) {
+ message.getView().setLayoutParams(
+ mMessageContainer.generateDefaultLayoutParams());
+ }
+ mMessageContainer.addView(message.getView(), textMessageIndex);
+ }
+ if (isImage) {
+ ((MessagingImageMessage) message).setIsolated(false);
+ }
+ // Let's sort them properly
+ if (textMessageIndex != mMessageContainer.indexOfChild(message.getView())) {
+ mMessageContainer.removeView(message.getView());
+ mMessageContainer.addView(message.getView(), textMessageIndex);
+ }
+ textMessageIndex++;
}
- message.setTextColor(mTextColor);
}
+ if (isolatedMessage != null) {
+ if (removeFromParentIfDifferent(isolatedMessage, mImageContainer)) {
+ mImageContainer.removeAllViews();
+ mImageContainer.addView(isolatedMessage.getView());
+ }
+ isolatedMessage.setIsolated(true);
+ } else if (mIsolatedMessage != null) {
+ mImageContainer.removeAllViews();
+ }
+ mIsolatedMessage = isolatedMessage;
mMessages = group;
}
+ /**
+ * Remove the message from the parent if the parent isn't the one provided
+ * @return whether the message was removed
+ */
+ private boolean removeFromParentIfDifferent(MessagingMessage message, ViewGroup newParent) {
+ ViewParent parent = message.getView().getParent();
+ if (parent != newParent) {
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(message.getView());
+ }
+ return true;
+ }
+ return false;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -317,13 +401,14 @@
@Override
public boolean onPreDraw() {
for (MessagingMessage message : mAddedMessages) {
- if (!message.isShown()) {
+ if (!message.getView().isShown()) {
continue;
}
- MessagingPropertyAnimator.fadeIn(message);
+ MessagingPropertyAnimator.fadeIn(message.getView());
if (!mFirstLayout) {
- MessagingPropertyAnimator.startLocalTranslationFrom(message,
- message.getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN);
+ MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(),
+ message.getView().getHeight(),
+ MessagingLayout.LINEAR_OUT_SLOW_IN);
}
}
mAddedMessages.clear();
@@ -333,6 +418,7 @@
});
}
mFirstLayout = false;
+ updateClipRect();
}
/**
@@ -372,6 +458,10 @@
return mMessageContainer;
}
+ public MessagingImageMessage getIsolatedMessage() {
+ return mIsolatedMessage;
+ }
+
public boolean needsGeneratedAvatar() {
return mNeedsGeneratedAvatar;
}
@@ -379,4 +469,19 @@
public Notification.Person getSender() {
return mSender;
}
+
+ public void setTransformingImages(boolean transformingImages) {
+ mTransformingImages = transformingImages;
+ }
+
+ public void setDisplayAvatarsAtEnd(boolean atEnd) {
+ if (mAvatarsAtEnd != atEnd) {
+ mAvatarsAtEnd = atEnd;
+ mImageContainer.setVisibility(atEnd ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ public List<MessagingMessage> getMessages() {
+ return mMessages;
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
new file mode 100644
index 0000000..961f90a
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -0,0 +1,273 @@
+/*
+ * 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.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pools;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+
+import java.io.IOException;
+
+/**
+ * A message of a {@link MessagingLayout} that is an image.
+ */
+@RemoteViews.RemoteView
+public class MessagingImageMessage extends ImageView implements MessagingMessage {
+ private static final String TAG = "MessagingImageMessage";
+ private static Pools.SimplePool<MessagingImageMessage> sInstancePool
+ = new Pools.SynchronizedPool<>(10);
+ private final MessagingMessageState mState = new MessagingMessageState(this);
+ private final int mMinImageHeight;
+ private final Path mPath = new Path();
+ private final int mImageRounding;
+ private final int mMaxImageHeight;
+ private final int mIsolatedSize;
+ private final int mExtraSpacing;
+ private Drawable mDrawable;
+ private float mAspectRatio;
+ private int mActualWidth;
+ private int mActualHeight;
+ private boolean mIsIsolated;
+
+ public MessagingImageMessage(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mMinImageHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_min_size);
+ mMaxImageHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_max_height);
+ mImageRounding = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_rounding);
+ mExtraSpacing = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_extra_spacing);
+ setMaxHeight(mMaxImageHeight);
+ mIsolatedSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+ }
+
+ @Override
+ public MessagingMessageState getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean setMessage(Notification.MessagingStyle.Message message) {
+ MessagingMessage.super.setMessage(message);
+ Drawable drawable;
+ try {
+ drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ int intrinsicHeight = drawable.getIntrinsicHeight();
+ if (intrinsicHeight == 0) {
+ Log.w(TAG, "Drawable with 0 intrinsic height was returned");
+ return false;
+ }
+ mDrawable = drawable;
+ mAspectRatio = ((float) mDrawable.getIntrinsicWidth()) / intrinsicHeight;
+ setImageDrawable(drawable);
+ setContentDescription(message.getText());
+ return true;
+ }
+
+ static MessagingMessage createMessage(MessagingLayout layout,
+ Notification.MessagingStyle.Message m) {
+ MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
+ MessagingImageMessage createdMessage = sInstancePool.acquire();
+ if (createdMessage == null) {
+ createdMessage = (MessagingImageMessage) LayoutInflater.from(
+ layout.getContext()).inflate(
+ R.layout.notification_template_messaging_image_message,
+ messagingLinearLayout,
+ false);
+ createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
+ }
+ boolean created = createdMessage.setMessage(m);
+ if (!created) {
+ createdMessage.recycle();
+ return MessagingTextMessage.createMessage(layout, m);
+ }
+ return createdMessage;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.clipPath(getRoundedRectPath());
+ int width = (int) Math.max(getActualWidth(), getActualHeight() * mAspectRatio);
+ int height = (int) (width / mAspectRatio);
+ int left = (int) ((getActualWidth() - width) / 2.0f);
+ mDrawable.setBounds(left, 0, left + width, height);
+ mDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ public Path getRoundedRectPath() {
+ int left = 0;
+ int right = getActualWidth();
+ int top = 0;
+ int bottom = getActualHeight();
+ mPath.reset();
+ int width = right - left;
+ float roundnessX = mImageRounding;
+ float roundnessY = mImageRounding;
+ roundnessX = Math.min(width / 2, roundnessX);
+ roundnessY = Math.min((bottom - top) / 2, roundnessY);
+ mPath.moveTo(left, top + roundnessY);
+ mPath.quadTo(left, top, left + roundnessX, top);
+ mPath.lineTo(right - roundnessX, top);
+ mPath.quadTo(right, top, right, top + roundnessY);
+ mPath.lineTo(right, bottom - roundnessY);
+ mPath.quadTo(right, bottom, right - roundnessX, bottom);
+ mPath.lineTo(left + roundnessX, bottom);
+ mPath.quadTo(left, bottom, left, bottom - roundnessY);
+ mPath.close();
+ return mPath;
+ }
+
+ public void recycle() {
+ MessagingMessage.super.recycle();
+ setAlpha(1.0f);
+ setTranslationY(0);
+ setImageBitmap(null);
+ mDrawable = null;
+ sInstancePool.release(this);
+ }
+
+ public static void dropCache() {
+ sInstancePool = new Pools.SynchronizedPool<>(10);
+ }
+
+ @Override
+ public int getMeasuredType() {
+ int measuredHeight = getMeasuredHeight();
+ int minImageHeight;
+ if (mIsIsolated) {
+ minImageHeight = mIsolatedSize;
+ } else {
+ minImageHeight = mMinImageHeight;
+ }
+ boolean measuredTooSmall = measuredHeight < minImageHeight
+ && measuredHeight != mDrawable.getIntrinsicHeight();
+ if (measuredTooSmall) {
+ return MEASURED_TOO_SMALL;
+ } else {
+ if (!mIsIsolated && measuredHeight != mDrawable.getIntrinsicHeight()) {
+ return MEASURED_SHORTENED;
+ } else {
+ return MEASURED_NORMAL;
+ }
+ }
+ }
+
+ @Override
+ public void setMaxDisplayedLines(int lines) {
+ // Nothing to do, this should be handled automatically.
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mIsIsolated) {
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+ MeasureSpec.getSize(heightMeasureSpec));
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // TODO: ensure that this isn't called when transforming
+ setActualWidth(getStaticWidth());
+ setActualHeight(getHeight());
+ }
+
+ @Override
+ public int getConsumedLines() {
+ return 3;
+ }
+
+ public void setActualWidth(int actualWidth) {
+ mActualWidth = actualWidth;
+ invalidate();
+ }
+
+ public int getActualWidth() {
+ return mActualWidth;
+ }
+
+ public void setActualHeight(int actualHeight) {
+ mActualHeight = actualHeight;
+ invalidate();
+ }
+
+ public int getActualHeight() {
+ return mActualHeight;
+ }
+
+ public int getStaticWidth() {
+ if (mIsIsolated) {
+ return getWidth();
+ }
+ return (int) (getHeight() * mAspectRatio);
+ }
+
+ public void setIsolated(boolean isolated) {
+ if (mIsIsolated != isolated) {
+ mIsIsolated = isolated;
+ // update the layout params not to have margins
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) getLayoutParams();
+ layoutParams.topMargin = isolated ? 0 : mExtraSpacing;
+ setLayoutParams(layoutParams);
+ }
+ }
+
+ @Override
+ public int getExtraSpacing() {
+ return mExtraSpacing;
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index d45c086..5279636 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -81,6 +81,7 @@
private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
private Notification.Person mUser;
private CharSequence mNameReplacement;
+ private boolean mIsCollapsed;
public MessagingLayout(@NonNull Context context) {
super(context);
@@ -127,6 +128,11 @@
}
@RemotableViewMethod
+ public void setIsCollapsed(boolean isCollapsed) {
+ mIsCollapsed = isCollapsed;
+ }
+
+ @RemotableViewMethod
public void setData(Bundle extras) {
Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
List<Notification.MessagingStyle.Message> newMessages
@@ -331,6 +337,7 @@
newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
mAddedGroups.add(newGroup);
}
+ newGroup.setDisplayAvatarsAtEnd(mIsCollapsed);
newGroup.setLayoutColor(mLayoutColor);
Notification.Person sender = senders.get(groupIndex);
CharSequence nameOverride = null;
@@ -392,7 +399,6 @@
MessagingMessage message = findAndRemoveMatchingMessage(m);
if (message == null) {
message = MessagingMessage.createMessage(this, m);
- message.addOnLayoutChangeListener(MESSAGING_PROPERTY_ANIMATOR);
}
message.setIsHistoric(historic);
result.add(message);
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index f0ef370..991e3e7 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -75,7 +75,6 @@
targetHeight = Integer.MAX_VALUE;
break;
}
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
// Now that we know which views to take, fix up the indents and see what width we get.
int measuredWidth = mPaddingLeft + mPaddingRight;
@@ -90,7 +89,6 @@
totalHeight = mPaddingTop + mPaddingBottom;
boolean first = true;
int linesRemaining = mMaxDisplayedLines;
-
// Starting from the bottom: we measure every view as if it were the only one. If it still
// fits, we take it, otherwise we stop there.
for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
@@ -100,11 +98,13 @@
final View child = getChildAt(i);
LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
MessagingChild messagingChild = null;
+ int spacing = mSpacing;
if (child instanceof MessagingChild) {
messagingChild = (MessagingChild) child;
messagingChild.setMaxDisplayedLines(linesRemaining);
+ spacing += messagingChild.getExtraSpacing();
}
- int spacing = first ? 0 : mSpacing;
+ spacing = first ? 0 : spacing;
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, totalHeight
- mPaddingTop - mPaddingBottom + spacing);
@@ -254,6 +254,9 @@
void setMaxDisplayedLines(int lines);
void hideAnimated();
boolean isHidingAnimated();
+ default int getExtraSpacing() {
+ return 0;
+ }
}
public static class LayoutParams extends MarginLayoutParams {
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index f09621f..bf1c5ca 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -16,182 +16,125 @@
package com.android.internal.widget;
-import android.annotation.AttrRes;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StyleRes;
import android.app.Notification;
-import android.content.Context;
-import android.text.Layout;
-import android.util.AttributeSet;
-import android.util.Pools;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.RemoteViews;
-
-import com.android.internal.R;
+import android.view.View;
import java.util.Objects;
/**
* A message of a {@link MessagingLayout}.
*/
-@RemoteViews.RemoteView
-public class MessagingMessage extends ImageFloatingTextView implements
- MessagingLinearLayout.MessagingChild {
+public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
- private static Pools.SimplePool<MessagingMessage> sInstancePool
- = new Pools.SynchronizedPool<>(10);
- private Notification.MessagingStyle.Message mMessage;
- private MessagingGroup mGroup;
- private boolean mIsHistoric;
- private boolean mIsHidingAnimated;
+ /**
+ * Prefix for supported image MIME types
+ **/
+ String IMAGE_MIME_TYPE_PREFIX = "image/";
- public MessagingMessage(@NonNull Context context) {
- super(context);
+ static MessagingMessage createMessage(MessagingLayout layout,
+ Notification.MessagingStyle.Message m) {
+ if (hasImage(m)) {
+ return MessagingImageMessage.createMessage(layout, m);
+ } else {
+ return MessagingTextMessage.createMessage(layout, m);
+ }
}
- public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
+ static void dropCache() {
+ MessagingTextMessage.dropCache();
+ MessagingImageMessage.dropCache();
}
- public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ static boolean hasImage(Notification.MessagingStyle.Message m) {
+ return m.getDataUri() != null
+ && m.getDataMimeType() != null
+ && m.getDataMimeType().startsWith(IMAGE_MIME_TYPE_PREFIX);
}
- public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ /**
+ * Set a message for this view.
+ * @return true if setting the message worked
+ */
+ default boolean setMessage(Notification.MessagingStyle.Message message) {
+ getState().setMessage(message);
+ return true;
}
- private void setMessage(Notification.MessagingStyle.Message message) {
- mMessage = message;
- setText(message.getText());
+ default Notification.MessagingStyle.Message getMessage() {
+ return getState().getMessage();
}
- public Notification.MessagingStyle.Message getMessage() {
- return mMessage;
- }
-
- boolean sameAs(Notification.MessagingStyle.Message message) {
- if (!Objects.equals(message.getText(), mMessage.getText())) {
+ default boolean sameAs(Notification.MessagingStyle.Message message) {
+ Notification.MessagingStyle.Message ownMessage = getMessage();
+ if (!Objects.equals(message.getText(), ownMessage.getText())) {
return false;
}
- if (!Objects.equals(message.getSender(), mMessage.getSender())) {
+ if (!Objects.equals(message.getSender(), ownMessage.getSender())) {
return false;
}
- if (!Objects.equals(message.getTimestamp(), mMessage.getTimestamp())) {
+ if (!Objects.equals(message.getTimestamp(), ownMessage.getTimestamp())) {
+ return false;
+ }
+ if (!Objects.equals(message.getDataMimeType(), ownMessage.getDataMimeType())) {
+ return false;
+ }
+ if (!Objects.equals(message.getDataUri(), ownMessage.getDataUri())) {
return false;
}
return true;
}
- boolean sameAs(MessagingMessage message) {
+ default boolean sameAs(MessagingMessage message) {
return sameAs(message.getMessage());
}
- static MessagingMessage createMessage(MessagingLayout layout,
- Notification.MessagingStyle.Message m) {
- MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
- MessagingMessage createdMessage = sInstancePool.acquire();
- if (createdMessage == null) {
- createdMessage = (MessagingMessage) LayoutInflater.from(layout.getContext()).inflate(
- R.layout.notification_template_messaging_message, messagingLinearLayout,
- false);
- }
- createdMessage.setMessage(m);
- return createdMessage;
+ default void removeMessage() {
+ getGroup().removeMessage(this);
}
- public void removeMessage() {
- mGroup.removeMessage(this);
+ default void setMessagingGroup(MessagingGroup group) {
+ getState().setGroup(group);
}
- public void recycle() {
- mGroup = null;
- mMessage = null;
- setAlpha(1.0f);
- setTranslationY(0);
- sInstancePool.release(this);
+ default void setIsHistoric(boolean isHistoric) {
+ getState().setIsHistoric(isHistoric);
}
- public void setMessagingGroup(MessagingGroup group) {
- mGroup = group;
+ default MessagingGroup getGroup() {
+ return getState().getGroup();
}
- public static void dropCache() {
- sInstancePool = new Pools.SynchronizedPool<>(10);
- }
-
- public void setIsHistoric(boolean isHistoric) {
- mIsHistoric = isHistoric;
- }
-
- public MessagingGroup getGroup() {
- return mGroup;
+ default void setIsHidingAnimated(boolean isHiding) {
+ getState().setIsHidingAnimated(isHiding);
}
@Override
- public int getMeasuredType() {
- boolean measuredTooSmall = getMeasuredHeight()
- < getLayoutHeight() + getPaddingTop() + getPaddingBottom();
- if (measuredTooSmall) {
- return MEASURED_TOO_SMALL;
- } else {
- Layout layout = getLayout();
- if (layout == null) {
- return MEASURED_TOO_SMALL;
- }
- if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
- return MEASURED_SHORTENED;
- } else {
- return MEASURED_NORMAL;
- }
- }
+ default boolean isHidingAnimated() {
+ return getState().isHidingAnimated();
}
@Override
- public void hideAnimated() {
+ default void hideAnimated() {
setIsHidingAnimated(true);
- mGroup.performRemoveAnimation(this, () -> setIsHidingAnimated(false));
+ getGroup().performRemoveAnimation(getState().getHostView(),
+ () -> setIsHidingAnimated(false));
}
- private void setIsHidingAnimated(boolean isHiding) {
- ViewParent parent = getParent();
- mIsHidingAnimated = isHiding;
- invalidate();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).invalidate();
- }
- }
-
- @Override
- public boolean isHidingAnimated() {
- return mIsHidingAnimated;
- }
-
- @Override
- public void setMaxDisplayedLines(int lines) {
- setMaxLines(lines);
- }
-
- @Override
- public int getConsumedLines() {
- return getLineCount();
- }
-
- public int getLayoutHeight() {
- Layout layout = getLayout();
- if (layout == null) {
- return 0;
- }
- return layout.getHeight();
- }
-
- @Override
- public boolean hasOverlappingRendering() {
+ default boolean hasOverlappingRendering() {
return false;
}
+
+ default void recycle() {
+ getState().reset();
+ }
+
+ default View getView() {
+ return (View) this;
+ }
+
+ default void setColor(int textColor) {}
+
+ MessagingMessageState getState();
+
+ void setVisibility(int visibility);
}
diff --git a/core/java/com/android/internal/widget/MessagingMessageState.java b/core/java/com/android/internal/widget/MessagingMessageState.java
new file mode 100644
index 0000000..ac62472
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingMessageState.java
@@ -0,0 +1,81 @@
+/*
+ * 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.widget;
+
+import android.app.Notification;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ * Shared state and implementation for MessagingMessages. Used to share common implementations.
+ */
+public class MessagingMessageState {
+ private final View mHostView;
+ private Notification.MessagingStyle.Message mMessage;
+ private MessagingGroup mGroup;
+ private boolean mIsHistoric;
+ private boolean mIsHidingAnimated;
+
+ MessagingMessageState(View hostView) {
+ mHostView = hostView;
+ }
+
+ public void setMessage(Notification.MessagingStyle.Message message) {
+ mMessage = message;
+ }
+
+ public Notification.MessagingStyle.Message getMessage() {
+ return mMessage;
+ }
+
+ public void setGroup(MessagingGroup group) {
+ mGroup = group;
+ }
+
+ public MessagingGroup getGroup() {
+ return mGroup;
+ }
+
+ public void setIsHistoric(boolean isHistoric) {
+ mIsHistoric = isHistoric;
+ }
+
+ public void setIsHidingAnimated(boolean isHiding) {
+ ViewParent parent = mHostView.getParent();
+ mIsHidingAnimated = isHiding;
+ mHostView.invalidate();
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).invalidate();
+ }
+ }
+
+ public boolean isHidingAnimated() {
+ return mIsHidingAnimated;
+ }
+
+ public View getHostView() {
+ return mHostView;
+ }
+
+ public void reset() {
+ mIsHidingAnimated = false;
+ mIsHistoric = false;
+ mGroup = null;
+ mMessage = null;
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java
new file mode 100644
index 0000000..794cc1d
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingTextMessage.java
@@ -0,0 +1,145 @@
+/*
+ * 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.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.content.Context;
+import android.text.Layout;
+import android.util.AttributeSet;
+import android.util.Pools;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+
+import java.util.Objects;
+
+/**
+ * A message of a {@link MessagingLayout}.
+ */
+@RemoteViews.RemoteView
+public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage {
+
+ private static Pools.SimplePool<MessagingTextMessage> sInstancePool
+ = new Pools.SynchronizedPool<>(20);
+ private final MessagingMessageState mState = new MessagingMessageState(this);
+
+ public MessagingTextMessage(@NonNull Context context) {
+ super(context);
+ }
+
+ public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public MessagingMessageState getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean setMessage(Notification.MessagingStyle.Message message) {
+ MessagingMessage.super.setMessage(message);
+ setText(message.getText());
+ return true;
+ }
+
+ static MessagingMessage createMessage(MessagingLayout layout,
+ Notification.MessagingStyle.Message m) {
+ MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
+ MessagingTextMessage createdMessage = sInstancePool.acquire();
+ if (createdMessage == null) {
+ createdMessage = (MessagingTextMessage) LayoutInflater.from(
+ layout.getContext()).inflate(
+ R.layout.notification_template_messaging_text_message,
+ messagingLinearLayout,
+ false);
+ createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
+ }
+ createdMessage.setMessage(m);
+ return createdMessage;
+ }
+
+ public void recycle() {
+ MessagingMessage.super.recycle();
+ setAlpha(1.0f);
+ setTranslationY(0);
+ sInstancePool.release(this);
+ }
+
+ public static void dropCache() {
+ sInstancePool = new Pools.SynchronizedPool<>(10);
+ }
+
+ @Override
+ public int getMeasuredType() {
+ boolean measuredTooSmall = getMeasuredHeight()
+ < getLayoutHeight() + getPaddingTop() + getPaddingBottom();
+ if (measuredTooSmall) {
+ return MEASURED_TOO_SMALL;
+ } else {
+ Layout layout = getLayout();
+ if (layout == null) {
+ return MEASURED_TOO_SMALL;
+ }
+ if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
+ return MEASURED_SHORTENED;
+ } else {
+ return MEASURED_NORMAL;
+ }
+ }
+ }
+
+ @Override
+ public void setMaxDisplayedLines(int lines) {
+ setMaxLines(lines);
+ }
+
+ @Override
+ public int getConsumedLines() {
+ return getLineCount();
+ }
+
+ public int getLayoutHeight() {
+ Layout layout = getLayout();
+ if (layout == null) {
+ return 0;
+ }
+ return layout.getHeight();
+ }
+
+ @Override
+ public void setColor(int color) {
+ setTextColor(color);
+ }
+}
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index ad6020c..7d1c706 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -98,6 +98,8 @@
if (mPayload != null) {
dest.writeInt(mPayload.length);
dest.writeByteArray(mPayload);
+ } else {
+ dest.writeInt(0);
}
}
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index fb18669..efd98e2 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -164,7 +164,7 @@
.append("Revision: ")
.append(SystemProperties.get("ro.revision", "")).append("\n")
.append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
- .append("Radio: ").append(Build.RADIO).append("\n")
+ .append("Radio: ").append(Build.getRadioVersion()).append("\n")
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
diff --git a/core/java/com/android/server/backup/SliceBackupHelper.java b/core/java/com/android/server/backup/SliceBackupHelper.java
new file mode 100644
index 0000000..8e5a5ee
--- /dev/null
+++ b/core/java/com/android/server/backup/SliceBackupHelper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.backup;
+
+import android.app.backup.BlobBackupHelper;
+import android.app.slice.ISliceManager;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+public class SliceBackupHelper extends BlobBackupHelper {
+ static final String TAG = "SliceBackupHelper";
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ // Current version of the blob schema
+ static final int BLOB_VERSION = 1;
+
+ // Key under which the payload blob is stored
+ static final String KEY_SLICES = "slices";
+
+ public SliceBackupHelper(Context context) {
+ super(BLOB_VERSION, KEY_SLICES);
+ // context is currently unused
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ byte[] newPayload = null;
+ if (KEY_SLICES.equals(key)) {
+ try {
+ ISliceManager sm = ISliceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SLICE_SERVICE));
+ // TODO: http://b/22388012
+ newPayload = sm.getBackupPayload(UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ // Treat as no data
+ Slog.e(TAG, "Couldn't communicate with slice manager");
+ newPayload = null;
+ }
+ }
+ return newPayload;
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (DEBUG) Slog.v(TAG, "Got restore of " + key);
+
+ if (KEY_SLICES.equals(key)) {
+ try {
+ ISliceManager sm = ISliceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SLICE_SERVICE));
+ // TODO: http://b/22388012
+ sm.applyRestore(payload, UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ Slog.e(TAG, "Couldn't communicate with slice manager");
+ }
+ }
+ }
+
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index a96b5dd..47e7a0e7 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -51,6 +51,7 @@
private static final String USAGE_STATS_HELPER = "usage_stats";
private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
+ private static final String SLICES_HELPER = "slices";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -88,6 +89,7 @@
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+ addHelper(SLICES_HELPER, new SliceBackupHelper(this));
super.onBackup(oldState, data, newState);
}
@@ -116,6 +118,7 @@
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
+ addHelper(SLICES_HELPER, new SliceBackupHelper(this));
super.onRestore(data, appVersionCode, newState);
}
diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS
index 6f77e04..ce50558 100644
--- a/core/java/com/android/server/net/OWNERS
+++ b/core/java/com/android/server/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 33f80ce..78a3e13 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -110,8 +110,8 @@
"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 @@
"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 d202173..4a032c4 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -123,6 +123,7 @@
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);
@@ -1346,6 +1347,7 @@
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 c88cf5c..ba56d59 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -26,10 +26,11 @@
#include <SkPictureRecorder.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/Canvas.h>
+#include <utils/Looper.h>
using namespace android;
-static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID;
+static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -123,9 +124,9 @@
return drawable->start();
}
-static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->stop();
+ return drawable->stop();
}
// Java's LOOP_INFINITE relies on this being the same.
@@ -137,33 +138,63 @@
drawable->setRepetitionCount(loopCount);
}
-class JniAnimationEndListener : public OnAnimationEndListener {
+class InvokeListener : public MessageHandler {
public:
- JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
+ InvokeListener(JNIEnv* env, jobject javaObject) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
- mJavaObject = env->NewGlobalRef(javaObject);
+ // Hold a weak reference to break a cycle that would prevent GC.
+ mWeakRef = env->NewWeakGlobalRef(javaObject);
}
- ~JniAnimationEndListener() override {
+ ~InvokeListener() override {
auto* env = get_env_or_die(mJvm);
- env->DeleteGlobalRef(mJavaObject);
+ env->DeleteWeakGlobalRef(mWeakRef);
}
- void onAnimationEnd() override {
+ virtual void handleMessage(const Message&) override {
auto* env = get_env_or_die(mJvm);
- env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID);
+ jobject localRef = env->NewLocalRef(mWeakRef);
+ if (localRef) {
+ env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
+ }
}
private:
JavaVM* mJvm;
- jobject mJavaObject;
+ jweak mWeakRef;
+};
+
+class JniAnimationEndListener : public OnAnimationEndListener {
+public:
+ JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
+ mListener = new InvokeListener(env, javaObject);
+ mLooper = std::move(looper);
+ }
+
+ void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
+
+private:
+ sp<InvokeListener> mListener;
+ sp<Looper> mLooper;
};
static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
jlong nativePtr, jobject jdrawable) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>(
- new JniAnimationEndListener(env, jdrawable)));
+ if (!jdrawable) {
+ drawable->setOnAnimationEndListener(nullptr);
+ } else {
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ doThrowISE(env,
+ "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
+ "looper!");
+ return;
+ }
+
+ drawable->setOnAnimationEndListener(
+ std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
+ }
}
static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
@@ -186,7 +217,7 @@
{ "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
{ "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
{ "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
- { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop },
+ { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
{ "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount },
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
@@ -195,7 +226,7 @@
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
- gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V");
+ gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dd3e6f0..ed032c7 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -28,7 +28,7 @@
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"
@@ -44,11 +44,9 @@
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
- : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
- allowUnsupportedFont(false) {}
+ : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)) {}
uint32_t langId;
minikin::FontFamily::Variant variant;
- bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
};
@@ -70,22 +68,17 @@
}
std::unique_ptr<NativeFamilyBuilder> builder(
reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
+ if (builder->fonts.empty()) {
+ return 0;
+ }
std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
builder->langId, builder->variant, std::move(builder->fonts));
- if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
+ if (family->getCoverage().length() == 0) {
return 0;
}
return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
}
-static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
- if (builderPtr == 0) {
- return;
- }
- NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- builder->allowUnsupportedFont = true;
-}
-
static void FontFamily_abort(jlong builderPtr) {
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
delete builder;
@@ -97,7 +90,7 @@
}
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
- jint givenWeight, jint givenItalic) {
+ jint weight, jint italic) {
uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
for (const auto& axis : builder->axes) {
skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
@@ -121,27 +114,15 @@
std::shared_ptr<minikin::MinikinFont> minikinFont =
std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
builder->axes);
+ minikin::Font::Builder fontBuilder(minikinFont);
- int weight = givenWeight;
- bool italic = givenItalic == 1;
- if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) {
- int os2Weight;
- bool os2Italic;
- if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) {
- ALOGE("analyzeStyle failed. Using default style");
- os2Weight = 400;
- os2Italic = false;
- }
- if (givenWeight == RESOLVE_BY_FONT_TABLE) {
- weight = os2Weight;
- }
- if (givenItalic == RESOLVE_BY_FONT_TABLE) {
- italic = os2Italic;
- }
+ if (weight != RESOLVE_BY_FONT_TABLE) {
+ fontBuilder.setWeight(weight);
}
-
- builder->fonts.push_back(minikin::Font(minikinFont,
- minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
+ if (italic != RESOLVE_BY_FONT_TABLE) {
+ fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0));
+ }
+ builder->fonts.push_back(fontBuilder.build());
builder->axes.clear();
return true;
}
@@ -224,7 +205,8 @@
NPE_CHECK_RETURN_ZERO(env, jpath);
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
+
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
if (NULL == mgr) {
builder->axes.clear();
return false;
@@ -236,27 +218,33 @@
return false;
}
- Asset* asset;
- if (isAsset) {
- asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- } else {
- asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(),
- Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> 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<ApkAssetsCookie>(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<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset));
+ sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
+ asset.release()));
return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}
@@ -270,7 +258,6 @@
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
{ "nCreateFamily", "(J)J", (void*)FontFamily_create },
- { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
{ "nAbort", "(J)V", (void*)FontFamily_abort },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 115d0d5..482d028 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -576,7 +576,7 @@
minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
float saveSkewX = paint->getTextSkewX();
bool savefakeBold = paint->isFakeBoldText();
- MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
+ MinikinFontSkia::populateSkPaint(paint, baseFont.font->typeface().get(), baseFont.fakery);
SkScalar spacing = paint->getFontMetrics(metrics);
// The populateSkPaint call may have changed fake bold / text skew
// because we want to measure with those effects applied, so now
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 09e37e1..49a24a3 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -361,7 +361,7 @@
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 0000000..c0f151b
--- /dev/null
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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 "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "android-base/unique_fd.h"
+#include "androidfw/ApkAssets.h"
+#include "utils/misc.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;
+ }
+
+ std::unique_ptr<const ApkAssets> 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<jlong>(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;
+ }
+
+ 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<const ApkAssets> 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<jlong>(apk_assets.release());
+}
+
+static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ delete reinterpret_cast<ApkAssets*>(ptr);
+}
+
+static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return env->NewStringUTF(apk_assets->GetPath().c_str());
+}
+
+static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+}
+
+static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(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<const ApkAssets*>(ptr);
+ std::unique_ptr<Asset> 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<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(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<jlong>(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_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 6b961f5..06de5da 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -573,6 +573,11 @@
minikin::Layout::purgeCaches();
}
+static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
+ Canvas::setCompatibilityVersion(apiLevel);
+}
+
+
}; // namespace CanvasJNI
static const JNINativeMethod gMethods[] = {
@@ -580,6 +585,7 @@
{"nInitRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
{"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches},
{"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches},
+ {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion},
// ------------ @FastNative ----------------
{"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index c79f5bd..12da273 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -36,6 +36,7 @@
#define ENCODING_AAC_ELD 15
#define ENCODING_AAC_XHE 16
#define ENCODING_AC4 17
+#define ENCODING_E_AC3_JOC 18
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -80,6 +81,8 @@
return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
case ENCODING_AC4:
return AUDIO_FORMAT_AC4;
+ // case ENCODING_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return AUDIO_FORMAT_E_AC3_JOC;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -130,6 +133,8 @@
// return ENCODING_AAC_XHE;
case AUDIO_FORMAT_AC4:
return ENCODING_AC4;
+ // case AUDIO_FORMAT_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // 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 afbc579..61a22c1 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1262,6 +1262,18 @@
return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
}
+static int android_media_AudioTrack_setPresentation(
+ JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId);
+}
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1333,6 +1345,7 @@
{"native_getVolumeShaperState",
"(I)Landroid/media/VolumeShaper$State;",
(void *)android_media_AudioTrack_get_volume_shaper_state},
+ {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
};
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1659168..e8ef349 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -32,10 +32,13 @@
static jclass gHashMapClazz;
static jmethodID gHashMapInit;
static jmethodID gHashMapPut;
+static jclass gLongClazz;
+static jmethodID gLongValueOf;
namespace android {
using vintf::HalManifest;
+using vintf::Level;
using vintf::SchemaType;
using vintf::VintfObject;
using vintf::XmlConverter;
@@ -154,6 +157,14 @@
return jMap;
}
+static jobject android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion(JNIEnv* env, jclass) {
+ std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+ if (manifest == nullptr || manifest->level() == Level::UNSPECIFIED) {
+ return nullptr;
+ }
+ return env->CallStaticObjectMethod(gLongClazz, gLongValueOf, static_cast<jlong>(manifest->level()));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gVintfObjectMethods[] = {
@@ -163,6 +174,7 @@
{"getHalNamesAndVersions", "()[Ljava/lang/String;", (void*)android_os_VintfObject_getHalNamesAndVersions},
{"getSepolicyVersion", "()Ljava/lang/String;", (void*)android_os_VintfObject_getSepolicyVersion},
{"getVndkSnapshots", "()Ljava/util/Map;", (void*)android_os_VintfObject_getVndkSnapshots},
+ {"getTargetFrameworkCompatibilityMatrixVersion", "()Ljava/lang/Long;", (void*)android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion},
};
const char* const kVintfObjectPathName = "android/os/VintfObject";
@@ -175,6 +187,8 @@
gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
"put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ gLongClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Long"));
+ gLongValueOf = GetStaticMethodIDOrDie(env, gLongClazz, "valueOf", "(J)Ljava/lang/Long;");
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
NELEM(gVintfObjectMethods));
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 683b4c4..c623ca6 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1,1851 +1,1441 @@
-/* //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 LOG_TAG "asset"
-#include <android_runtime/android_util_AssetManager.h>
-
#include <inttypes.h>
#include <linux/capability.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
-#include "androidfw/Asset.h"
-#include "androidfw/AssetManager.h"
-#include "androidfw/AttributeResolution.h"
-#include "androidfw/ResourceTypes.h"
+#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 "core_jni_helpers.h"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedStringChars.h>
-#include <nativehelper/ScopedUtfChars.h>
+#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/misc.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);
+// 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<jint>(cookie + 1) : -1;
+}
-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<jint>(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;
+constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
+ return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
}
// This is called by zygote (running as user root) as part of preloadResources.
-static void verifySystemIdmaps()
-{
- pid_t pid;
- char system_id[10];
+static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
+ switch (pid_t pid = fork()) {
+ case -1:
+ PLOG(ERROR) << "failed to fork for idmap";
+ break;
- snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
+ // child
+ case 0: {
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
- 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));
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
- capheader.version = _LINUX_CAPABILITY_VERSION;
- capheader.pid = 0;
+ if (capget(&capheader, &capdata) != 0) {
+ PLOG(ERROR) << "capget";
+ exit(1);
+ }
- if (capget(&capheader, &capdata) != 0) {
- ALOGE("capget: %s\n", strerror(errno));
- exit(1);
- }
+ capdata.effective = capdata.permitted;
+ if (capset(&capheader, &capdata) != 0) {
+ PLOG(ERROR) << "capset";
+ exit(1);
+ }
- capdata.effective = capdata.permitted;
- if (capset(&capheader, &capdata) != 0) {
- ALOGE("capset: %s\n", strerror(errno));
- exit(1);
- }
+ if (setgid(AID_SYSTEM) != 0) {
+ PLOG(ERROR) << "setgid";
+ exit(1);
+ }
- if (setgid(AID_SYSTEM) != 0) {
- ALOGE("setgid: %s\n", strerror(errno));
- exit(1);
- }
+ if (setuid(AID_SYSTEM) != 0) {
+ PLOG(ERROR) << "setuid";
+ 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;
- // 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;
- 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/<value of OVERLAY_THEME_DIR_PROPERTY> 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;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-
-// 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<AssetManager*>(amHandle);
- if (am != NULL) {
- return am;
- }
- jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
- return NULL;
-}
-
-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<jlong>(a);
-}
-
-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;
- }
-
- return newParcelFileDescriptor(env, fileDesc);
-}
-
-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);
-}
-
-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<int32_t>(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<jlong>(a);
-}
-
-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<int32_t>(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 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; i<N; i++) {
- const String8& name = dir->getFileName(i);
- jstring str = env->NewStringUTF(name.string());
- if (str == NULL) {
- delete dir;
- return NULL;
+ // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> 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();
}
- env->SetObjectArrayElement(array, i, str);
- env->DeleteLocalRef(str);
- }
+ }
- delete dir;
+ if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::OVERLAY_DIR;
+ }
- return array;
+ 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 void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- //printf("Destroying Asset Stream: %p\n", a);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return;
- }
-
- delete a;
-}
-
-static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(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 jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle, jbyteArray bArray,
- jint off, jint len)
-{
- Asset* a = reinterpret_cast<Asset*>(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<jint>(res);
-
- if (res < 0) {
- jniThrowException(env, "java/io/IOException", "");
- }
- return -1;
-}
-
-static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
- jlong assetHandle,
- jlong offset, jint whence)
-{
- Asset* a = reinterpret_cast<Asset*>(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 android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- return a->getLength();
-}
-
-static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
- jlong assetHandle)
-{
- Asset* a = reinterpret_cast<Asset*>(assetHandle);
-
- if (a == NULL) {
- jniThrowNullPointerException(env, "asset");
- return -1;
- }
-
- 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;
- }
-
- int32_t cookie;
- bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
-
- return (res) ? static_cast<jint>(cookie) : 0;
-}
-
-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 jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz,
- jobject fileDescriptor, jstring debugPathName,
- jboolean appAsLib)
-{
- ScopedUtfChars debugPathName8(env, debugPathName);
-
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (fd < 0) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
- return 0;
- }
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
-
- int dupfd = ::dup(fd);
- if (dupfd < 0) {
- jniThrowIOException(env, errno);
- return 0;
- }
-
- int32_t cookie;
- bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib);
-
- return (res) ? static_cast<jint>(cookie) : 0;
-}
-
-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 getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
-{
- Vector<String8> locales;
-
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return NULL;
- }
-
- am->getLocales(&locales, includeSystemLocales);
-
- const int N = locales.size();
-
- jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
- if (result == NULL) {
- return NULL;
- }
-
- for (int i=0; i<N; i++) {
- jstring str = env->NewStringUTF(locales[i].string());
- if (str == NULL) {
- return NULL;
- }
- env->SetObjectArrayElement(result, i, str);
- env->DeleteLocalRef(str);
- }
-
- return result;
-}
-
-static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
-{
- return getLocales(env, clazz, true /* include system locales */);
-}
-
-static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
-{
- return getLocales(env, clazz, false /* don't include system locales */);
-}
-
-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 jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
- const Vector<ResTable_config>& configs) {
- const int N = configs.size();
- jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
- if (result == NULL) {
- return NULL;
- }
-
- for (int i=0; i<N; i++) {
- jobject config = constructConfigurationObject(env, configs[i]);
- if (config == NULL) {
- env->DeleteLocalRef(result);
- return NULL;
- }
-
- env->SetObjectArrayElement(result, i, config);
- env->DeleteLocalRef(config);
- }
-
- return result;
-}
-
-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<ResTable_config> configs;
- res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
-
- return getSizeConfigurationsInternal(env, configs);
-}
-
-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 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<const char16_t*>(defType)
- ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL))
- : NULL;
- jsize defTypeLen = defType
- ? env->GetStringLength(defType) : 0;
- const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage)
- ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage,
- NULL))
- : NULL;
- jsize defPackageLen = defPackage
- ? env->GetStringLength(defPackage) : 0;
-
- jint ident = am->getResources().identifierForName(
- reinterpret_cast<const char16_t*>(name16.get()), name16.size(),
- defType16, defTypeLen, defPackage16, defPackageLen);
-
- if (defPackage16) {
- env->ReleaseStringChars(defPackage,
- reinterpret_cast<const jchar*>(defPackage16));
- }
- if (defType16) {
- env->ReleaseStringChars(defType,
- reinterpret_cast<const jchar*>(defType16));
- }
-
- return ident;
-}
-
-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 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 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);
- }
-
- return NULL;
-}
-
-static jstring android_content_AssetManager_getResourceEntryName(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.name8 != NULL) {
- return env->NewStringUTF(name.name8);
- }
-
- if (name.name != NULL) {
- return env->NewString((const jchar*)name.name, name.nameLen);
- }
-
- return NULL;
-}
-
-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<jint>(block);
-}
-
-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;
-
- const ResTable::bag_entry* entry = NULL;
- uint32_t typeSpecFlags;
- ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
-
- for (ssize_t i=0; i<entryCount; i++) {
- if (((uint32_t)bagEntryId) == entry->map.name.ident) {
- block = entry->stringBlock;
- value = entry->map.value;
- }
- entry++;
- }
-
- res.unlock();
-
- if (block < 0) {
- return static_cast<jint>(block);
- }
-
- 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);
- }
-
- return static_cast<jint>(block);
-}
-
-static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return am->getResources().getTableCount();
-}
-
-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<jlong>(am->getResources().getTableStringBlock(block));
-}
-
-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<int32_t>(cookie)));
- if (name.length() == 0) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
- return NULL;
- }
- jstring str = env->NewStringUTF(name.string());
- return str;
-}
-
-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<jint>(res.getBasePackageId(i)),
- env->NewString(reinterpret_cast<const jchar*>(name.string()),
- name.size()));
- }
- return sparseArray;
-}
-
-static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
-{
- AssetManager* am = assetManagerForJavaObject(env, clazz);
- if (am == NULL) {
- return 0;
- }
- return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources()));
-}
-
-static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
- jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- delete theme;
-}
-
-static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
- jlong themeHandle,
- jint styleRes,
- jboolean force)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- theme->applyStyle(styleRes, force ? true : false);
-}
-
-static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
- jlong destHandle, jlong srcHandle)
-{
- ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle);
- ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle);
- dest->setTo(*src);
-}
-
-static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(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<ResTable::Theme*>(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;
- }
- }
- }
- 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<ResTable::Theme*>(themeHandle);
- return theme->getChangingConfigurations();
-}
-
-static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
- jlong themeHandle, jint pri,
- jstring tag, jstring prefix)
-{
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
- const ResTable& res(theme->getResTable());
- (void)res;
-
- // XXX Need to use params.
- theme->dumpToLog();
-}
-
-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) {
- return JNI_FALSE;
- }
-
- jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
- const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
-
- 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);
- }
- }
-
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
- (uint32_t*) srcValues, NSV,
- (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(inValues, srcValues, 0);
- env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- 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<ResTable::Theme*>(themeToken);
- ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
- uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress));
- uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress));
- ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
- reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices);
- env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT);
-}
-
-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 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<jint>(bagOff);
-}
-
-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<jint>(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 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<int32_t>(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<jlong>(block);
-}
-
-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)<N; i++, bag++) {
- jint stringIndex = -1;
- jint stringBlock = 0;
- value = bag->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 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)<N; i++, bag++) {
- value = bag->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<const jchar*>(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 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)<N; i++, bag++) {
- value = bag->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 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;
- }
-
- const ResTable::bag_entry* bag = startOfBag;
- for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
- int resourceId = bag->map.name.ident;
- env->SetIntArrayRegion(array, i, 1, &resourceId);
- }
- res.unlockBag(startOfBag);
- return array;
-}
-
-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;
- }
-
- am->addDefaultAssets();
-
- ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
- env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
-}
-
-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);
- }
-}
-
-static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
-{
- return Asset::getGlobalCount();
-}
-
-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 jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
-{
- return AssetManager::getGlobalCount();
+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<jint>(ApkAssetsCookieToJavaCookie(cookie));
}
// ----------------------------------------------------------------------------
-/*
- * 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 },
+// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
+struct GuardedAssetManager : public ::AAssetManager {
+ Guarded<AssetManager2> guarded_assetmanager;
};
-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");
+::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;
+}
- 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");
+Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
+ if (assetmanager == nullptr) {
+ return nullptr;
+ }
+ return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
+}
- jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
- gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
+Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
+ return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
+}
- jclass stringClass = FindClassOrDie(env, "java/lang/String");
- g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
+ return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
+}
- jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
- gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
- gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
- "<init>", "()V");
- gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
- "(ILjava/lang/Object;)V");
+static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
+ jlongArray out_offsets) {
+ off64_t start_offset, length;
+ int fd = asset->openFileDescriptor(&start_offset, &length);
+ asset.reset();
- jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
- gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
- gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
- "<init>", "()V");
- gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
- "smallestScreenWidthDp", "I");
- gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
- "screenWidthDp", "I");
- gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
- "screenHeightDp", "I");
+ if (fd < 0) {
+ jniThrowException(env, "java/io/FileNotFoundException",
+ "This file can not be opened as a file descriptor; it is probably "
+ "compressed");
+ return nullptr;
+ }
- return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
- NELEM(gAssetManagerMethods));
+ jlong* offsets = reinterpret_cast<jlong*>(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 jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
+ return Asset::getGlobalCount();
+}
+
+static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
+ String8 alloc = Asset::getAssetAllocations();
+ if (alloc.length() <= 0) {
+ return nullptr;
+ }
+ return env->NewStringUTF(alloc.string());
+}
+
+static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
+ // TODO(adamlesinski): Switch to AssetManager2.
+ return AssetManager::getGlobalCount();
+}
+
+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<jlong>(new GuardedAssetManager());
+}
+
+static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ delete reinterpret_cast<GuardedAssetManager*>(ptr);
+}
+
+static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jobjectArray apk_assets_array, jboolean invalidate_caches) {
+ const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
+ std::vector<const ApkAssets*> 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;
+ }
+
+ jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+}
+
+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) {
+ ResTable_config configuration;
+ memset(&configuration, 0, sizeof(configuration));
+ configuration.mcc = static_cast<uint16_t>(mcc);
+ configuration.mnc = static_cast<uint16_t>(mnc);
+ configuration.orientation = static_cast<uint8_t>(orientation);
+ configuration.touchscreen = static_cast<uint8_t>(touchscreen);
+ configuration.density = static_cast<uint16_t>(density);
+ configuration.keyboard = static_cast<uint8_t>(keyboard);
+ configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
+ configuration.navigation = static_cast<uint8_t>(navigation);
+ configuration.screenWidth = static_cast<uint16_t>(screen_width);
+ configuration.screenHeight = static_cast<uint16_t>(screen_height);
+ configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
+ configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
+ configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
+ configuration.screenLayout = static_cast<uint8_t>(screen_layout);
+ configuration.uiMode = static_cast<uint8_t>(ui_mode);
+ configuration.colorMode = static_cast<uint8_t>(color_mode);
+ configuration.sdkVersion = static_cast<uint16_t>(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<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ assetmanager->SetConfiguration(configuration);
+}
+
+static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+
+ jobject sparse_array =
+ env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
+
+ if (sparse_array == nullptr) {
+ // An exception is pending.
+ return nullptr;
+ }
+
+ 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;
+ }
+
+ env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
+ jpackage_name);
+ });
+ return sparse_array;
+}
+
+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;
+ }
+
+ std::vector<std::string> all_file_paths;
+ {
+ StringPiece normalized_path = path_utf8.c_str();
+ if (normalized_path.data()[0] == '/') {
+ normalized_path = normalized_path.substr(1);
+ }
+ std::string root_path = StringPrintf("assets/%s", normalized_path.data());
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ for (const ApkAssets* assets : assetmanager->GetApkAssets()) {
+ assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) {
+ if (type == FileType::kFileTypeRegular) {
+ all_file_paths.push_back(file_path.to_string());
+ }
+ });
+ }
+ }
+
+ jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jsize index = 0;
+ for (const std::string& file_path : all_file_paths) {
+ jstring java_string = env->NewStringUTF(file_path.c_str());
+
+ // Check for errors creating the strings (if malformed or no memory).
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, index++, 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 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;
+ }
+
+ 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<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset =
+ assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+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;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> 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 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;
+ }
+
+ 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<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> asset;
+ if (cookie != kInvalidCookie) {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
+ static_cast<Asset::AccessMode>(access_mode));
+ } else {
+ asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
+ static_cast<Asset::AccessMode>(access_mode));
+ }
+
+ if (!asset) {
+ jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+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;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> 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 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;
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::unique_ptr<Asset> 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<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(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<jlong>(xml_tree.release());
+}
+
+static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jshort density, jobject typed_value,
+ jboolean resolve_references) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ ApkAssetsCookie cookie =
+ assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
+ static_cast<uint16_t>(density), &value, &selected_config, &flags);
+ if (cookie == kInvalidCookie) {
+ return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ }
+
+ uint32_t ref = static_cast<uint32_t>(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 NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jint bag_entry_id, jobject typed_value) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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 jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(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 jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(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<const jchar*>(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 jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count * 2);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(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<jint>(value.data);
+ }
+
+ buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
+ buffer[(i * 2) + 1] = string_index;
+ }
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ return array;
+}
+
+static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return nullptr;
+ }
+
+ jintArray array = env->NewIntArray(bag->entry_count);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(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<jint>(value.data);
+ }
+ }
+ env->ReleasePrimitiveArrayCritical(array, buffer, 0);
+ return array;
+}
+
+static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return -1;
+ }
+ return static_cast<jint>(bag->entry_count);
+}
+
+static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
+ jintArray out_data) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
+ if (bag == nullptr) {
+ return -1;
+ }
+
+ const jsize out_data_length = env->GetArrayLength(out_data);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
+ return -1;
+ }
+
+ jint* buffer = reinterpret_cast<jint*>(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<jint>(value.dataType);
+ cursor[STYLE_DATA] = static_cast<jint>(value.data);
+ cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
+ cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
+ cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
+ cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
+ cursor += STYLE_NUM_ENTRIES;
+ }
+ env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
+ return static_cast<jint>(bag->entry_count);
+}
+
+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<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
+}
+
+static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ std::string result;
+ if (name.package != nullptr) {
+ result.append(name.package, name.package_len);
+ }
+
+ if (name.type != nullptr || name.type16 != nullptr) {
+ if (!result.empty()) {
+ result += ":";
+ }
+
+ if (name.type != nullptr) {
+ result.append(name.type, name.type_len);
+ } else {
+ result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+ }
+ }
+
+ if (name.entry != nullptr || name.entry16 != nullptr) {
+ if (!result.empty()) {
+ result += "/";
+ }
+
+ if (name.entry != nullptr) {
+ result.append(name.entry, name.entry_len);
+ } else {
+ result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+ }
+ }
+ return env->NewStringUTF(result.c_str());
+}
+
+static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.package != nullptr) {
+ return env->NewStringUTF(name.package);
+ }
+ return nullptr;
+}
+
+static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.type != nullptr) {
+ return env->NewStringUTF(name.type);
+ } else if (name.type16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
+ }
+ return nullptr;
+}
+
+static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ AssetManager2::ResourceName name;
+ if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
+ return nullptr;
+ }
+
+ if (name.entry != nullptr) {
+ return env->NewStringUTF(name.entry);
+ } else if (name.entry16 != nullptr) {
+ return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
+ }
+ return nullptr;
+}
+
+static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
+ jboolean exclude_system) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::set<std::string> 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 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 jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ std::set<ResTable_config> configurations =
+ assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
+
+ jobjectArray array =
+ env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
+ if (array == nullptr) {
+ return nullptr;
+ }
+
+ size_t idx = 0;
+ for (const ResTable_config& configuration : configurations) {
+ jobject java_configuration = ConstructConfigurationObject(env, configuration);
+ if (java_configuration == nullptr) {
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(array, idx++, java_configuration);
+ env->DeleteLocalRef(java_configuration);
+ }
+ return array;
+}
+
+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<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+ uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
+ uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
+
+ jsize attrs_len = env->GetArrayLength(java_attrs);
+ jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return;
+ }
+
+ ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
+ static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ out_values, out_indices);
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+}
+
+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<jint*>(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<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
+ if (values == nullptr) {
+ env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return JNI_FALSE;
+ }
+ }
+
+ jint* out_values =
+ reinterpret_cast<jint*>(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<jint*>(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;
+ }
+ }
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ bool result = ResolveAttrs(
+ theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
+ reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
+ attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(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;
+}
+
+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<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
+ if (attrs == nullptr) {
+ return JNI_FALSE;
+ }
+
+ jint* out_values =
+ reinterpret_cast<jint*>(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<jint*>(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;
+ }
+ }
+ }
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+
+ bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
+ reinterpret_cast<uint32_t*>(attrs), attrs_len,
+ reinterpret_cast<uint32_t*>(out_values),
+ reinterpret_cast<uint32_t*>(out_indices));
+
+ 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 jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
+}
+
+static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
+ delete reinterpret_cast<Theme*>(theme_ptr);
+}
+
+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<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+ theme->ApplyStyle(static_cast<uint32_t>(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 void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
+ jlong src_theme_ptr) {
+ Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
+ Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
+ if (!dst_theme->SetTo(*src_theme)) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Themes are from different AssetManagers");
+ }
+}
+
+static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
+ reinterpret_cast<Theme*>(theme_ptr)->Clear();
+}
+
+static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint resid, jobject typed_value,
+ jboolean resolve_references) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(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 void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+ jint priority, jstring tag, jstring prefix) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ CHECK(theme->GetAssetManager() == &(*assetmanager));
+ (void) assetmanager;
+ (void) theme;
+ (void) priority;
+ (void) tag;
+ (void) prefix;
+}
+
+static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
+ jlong theme_ptr) {
+ Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+ return static_cast<jint>(theme->GetChangingConfigurations());
+}
+
+static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ delete reinterpret_cast<Asset*>(asset_ptr);
+}
+
+static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ uint8_t b;
+ ssize_t res = asset->read(&b, sizeof(b));
+ return res == sizeof(b) ? static_cast<jint>(b) : -1;
+}
+
+static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
+ jint offset, jint len) {
+ if (len == 0) {
+ return 0;
+ }
+
+ 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;
+ }
+
+ ScopedByteArrayRW byte_array(env, java_buffer);
+ if (byte_array.get() == nullptr) {
+ return -1;
+ }
+
+ Asset* asset = reinterpret_cast<Asset*>(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<jint>(res) : -1;
+}
+
+static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
+ jint whence) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->seek(
+ static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
+}
+
+static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->getLength());
+}
+
+static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
+ Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
+ return static_cast<jlong>(asset->getRemainingLength());
+}
+
+// ----------------------------------------------------------------------------
+
+// JNI registration.
+static const JNINativeMethod gAssetManagerMethods[] = {
+ // 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 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, "<init>", "()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, "<init>", "()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 1350f3f..5b788a6 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -673,7 +673,7 @@
nativeData->mObject = val;
gNativeDataCache = nullptr;
++gNumProxies;
- if (++gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
+ if (gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
ALOGW("Unexpectedly many live BinderProxies: %d\n", gNumProxies);
gProxiesWarned = gNumProxies;
}
diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h
index 8dd9337..2c1e357 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 <androidfw/AssetManager.h>
+#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<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
+extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager);
-}
+} // namespace android
-#endif
+#endif // ANDROID_RUNTIME_ASSETMANAGER_H
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8a04bf7..698f394 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -42,6 +42,7 @@
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/core/proto/android/service/usb.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
import "frameworks/base/libs/incident/proto/android/os/header.proto";
@@ -203,7 +204,10 @@
(section).args = "power --proto"
];
- optional android.service.print.PrintServiceDumpProto print = 3010;
+ optional android.service.print.PrintServiceDumpProto print = 3010 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "print --proto"
+ ];
optional android.service.procstats.ProcessStatsServiceDumpProto procstats = 3011 [
(section).type = SECTION_DUMPSYS,
@@ -254,4 +258,9 @@
(section).type = SECTION_DUMPSYS,
(section).args = "jobscheduler --proto"
];
+
+ optional android.service.usb.UsbServiceDumpProto usb = 3021 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "usb --proto"
+ ];
}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index bfd575a..ff0f4fd 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -208,7 +208,7 @@
optional SettingProto wifi_on = 136 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_scan_always_available = 137 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto wifi_wakeup_enabled = 138 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto wifi_wakeup_available = 305 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 305; // Removed wifi_wakeup_available
optional SettingProto network_scoring_ui_enabled = 306 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto speed_label_cache_eviction_age_millis = 307 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index aa2663f..d1c5db6 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -27,6 +27,7 @@
option java_multiple_files = true;
+// next ID: 43
message AlarmManagerServiceProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -103,18 +104,20 @@
repeated InFlightProto outstanding_deliveries = 34;
- // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. It
- // should be either CosntantsProto.allow_while_idle_short_duration_ms or
- // ConstantsProto.allow_while_idle_long_duration_ms.
- optional int64 allow_while_idle_min_duration_ms = 35;
-
message LastAllowWhileIdleDispatch {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 uid = 1;
// In the 'elapsed' timebase.
optional int64 time_ms = 2;
+
+ // Time when the next while-idle is allowed, in the 'elapsed' timebase.
+ optional int64 next_allowed_ms = 3;
}
+
+ // Whether the short or long while-idle timeout should be used for each UID.
+ repeated int32 use_allow_while_idle_short_time = 35;
+
// For each uid, this is the last time we dispatched an "allow while idle"
// alarm, used to determine the earliest we can dispatch the next such alarm.
repeated LastAllowWhileIdleDispatch last_allow_while_idle_dispatch_times = 36;
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 43c869c..5296e47 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -24,14 +24,19 @@
option java_multiple_files = true;
// Dump from com.android.server.ForceAppStandbyTracker.
+//
+// Next ID: 12
message ForceAppStandbyTrackerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Whether all apps are forced standby or not.
optional bool force_all_apps_standby = 1;
+ // UIDs currently active.
+ repeated int32 active_uids = 2;
+
// UIDs currently in the foreground.
- repeated int32 foreground_uids = 2;
+ repeated int32 foreground_uids = 11;
// App ids that are in power-save whitelist.
repeated int32 power_save_whitelist_app_ids = 3;
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index b5c3ac0..5cb5319 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -227,7 +227,6 @@
optional int32 setting_minimum = 1;
optional int32 setting_maximum = 2;
optional int32 setting_default = 3;
- optional int32 setting_for_vr_default = 4;
}
// True to decouple auto-suspend mode from the display state.
@@ -293,44 +292,27 @@
// The stay on while plugged in setting.
// A set of battery conditions under which to make the screen stay on.
optional StayOnWhilePluggedInProto stay_on_while_plugged_in = 29;
- // The screen brightness setting, from 0 to 255.
- // Use -1 if no value has been set.
- optional sint32 screen_brightness_setting = 30;
- // The screen auto-brightness adjustment setting, from -1 to 1.
- // Use 0 if there is no adjustment.
- optional float screen_auto_brightness_adjustment_setting = 31;
// The screen brightness mode.
- optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 32;
+ optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 30;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
- optional sint32 screen_brightness_override_from_window_manager = 33;
+ optional sint32 screen_brightness_override_from_window_manager = 31;
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity
// timeout. Use -1 to disable.
- optional sint64 user_activity_timeout_override_from_window_manager_ms = 34;
+ optional sint64 user_activity_timeout_override_from_window_manager_ms = 32;
// The window manager has determined the user to be inactive via other means.
// Set this to false to disable.
- optional bool is_user_inactive_override_from_window_manager = 35;
- // The screen brightness setting override from the settings application
- // to temporarily adjust the brightness until next updated,
- // Use -1 to disable.
- optional sint32 temporary_screen_brightness_setting_override = 36;
- // The screen brightness adjustment setting override from the settings
- // application to temporarily adjust the auto-brightness adjustment factor
- // until next updated, in the range -1..1.
- // Use NaN to disable.
- optional float temporary_screen_auto_brightness_adjustment_setting_override = 37;
+ optional bool is_user_inactive_override_from_window_manager = 33;
// The screen state to use while dozing.
- optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 38;
+ optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 34;
// The screen brightness to use while dozing.
- optional float dozed_screen_brightness_override_from_dream_manager = 39;
+ optional float dozed_screen_brightness_override_from_dream_manager = 35;
// Screen brightness settings limits.
- optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 40;
- // The screen brightness setting, from 0 to 255, to be used while in VR Mode.
- optional int32 screen_brightness_for_vr_setting = 41;
+ optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 36;
// True if double tap to wake is enabled
- optional bool is_double_tap_wake_enabled = 42;
+ optional bool is_double_tap_wake_enabled = 37;
// True if we are currently in VR Mode.
- optional bool is_vr_mode_enabled = 43;
+ optional bool is_vr_mode_enabled = 38;
}
diff --git a/core/proto/android/service/enums.proto b/core/proto/android/service/enums.proto
new file mode 100644
index 0000000..b64e685
--- /dev/null
+++ b/core/proto/android/service/enums.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.service;
+
+option java_outer_classname = "ServiceProtoEnums";
+option java_multiple_files = true;
+
+enum UsbEndPointType {
+ USB_ENDPOINT_TYPE_XFER_CONTROL = 0;
+ USB_ENDPOINT_TYPE_XFER_ISOC = 1;
+ USB_ENDPOINT_TYPE_XFER_BULK = 2;
+ USB_ENDPOINT_TYPE_XFER_INT = 3;
+}
+
+enum UsbEndPointDirection {
+ USB_ENDPOINT_DIR_OUT = 0;
+ USB_ENDPOINT_DIR_IN = 0x80;
+}
+
+enum UsbConnectionRecordMode {
+ USB_CONNECTION_RECORD_MODE_CONNECT = 0;
+ USB_CONNECTION_RECORD_MODE_CONNECT_BADPARSE = 1;
+ USB_CONNECTION_RECORD_MODE_CONNECT_BADDEVICE = 2;
+ USB_CONNECTION_RECORD_MODE_DISCONNECT = -1;
+}
\ No newline at end of file
diff --git a/core/proto/android/service/network_watchlist.proto b/core/proto/android/service/network_watchlist.proto
new file mode 100644
index 0000000..fb03b93
--- /dev/null
+++ b/core/proto/android/service/network_watchlist.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 com.android.service;
+
+option java_multiple_files = true;
+
+// It will be used by com.android.server.net.watchlist.ReportEncoder to
+// generate network watchlist report.
+message NetworkWatchlistReportProto {
+
+ optional int32 report_version = 1;
+
+ optional string watchlist_config_hash = 2;
+
+ repeated NetworkWatchlistAppResultProto app_result = 3;
+}
+
+message NetworkWatchlistAppResultProto {
+ optional string app_digest = 1;
+ optional bool encoded_result = 2;
+}
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index ef777de..f8050a1 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -49,7 +49,8 @@
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 user_id = 1;
- optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ // Name of the shared UID. eg: android.uid.bluetooth
+ optional string name = 2;
}
// Installed packages.
diff --git a/core/proto/android/service/print.proto b/core/proto/android/service/print.proto
index c2be7f1..f783b86 100644
--- a/core/proto/android/service/print.proto
+++ b/core/proto/android/service/print.proto
@@ -21,13 +21,18 @@
option java_outer_classname = "PrintServiceProto";
import "frameworks/base/core/proto/android/content/component_name.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
message PrintServiceDumpProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Each user has a separate printer state
repeated PrintUserStateProto userStates = 1;
}
message PrintUserStateProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Should be 0, 10, 11, 12, etc. where 0 is the owner.
optional int32 user_id = 1;
@@ -51,6 +56,8 @@
}
message PrintSpoolerStateProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Is the print spooler destroyed?
optional bool is_destroyed = 1;
@@ -62,6 +69,8 @@
}
message PrintSpoolerInternalStateProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Print jobs
repeated PrintJobInfoProto print_jobs = 1;
@@ -73,6 +82,8 @@
}
message PrinterCapabilitiesProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Minimum margins of the printer
optional MarginsProto min_margins = 1;
@@ -90,6 +101,8 @@
}
message PrinterInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// The id of the printer
optional PrinterIdProto id = 1;
@@ -120,6 +133,8 @@
}
message PrinterDiscoverySessionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Is this session destroyed?
optional bool is_destroyed = 1;
@@ -140,6 +155,8 @@
}
message InstalledPrintServiceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Component name of the service
optional android.content.ComponentNameProto component_name = 1;
@@ -154,14 +171,18 @@
}
message PrinterIdProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Component name of the service that reported the printer
optional android.content.ComponentNameProto service_name = 1;
// Local id of the printer
- optional string local_id = 2;
+ optional string local_id = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
}
message ActivePrintServiceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Component name of the service
optional android.content.ComponentNameProto component_name = 1;
@@ -185,6 +206,8 @@
}
message MediaSizeProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Id of this media size
optional string id = 1;
@@ -199,6 +222,8 @@
}
message ResolutionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Id of this resolution
optional string id = 1;
@@ -213,6 +238,8 @@
}
message MarginsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Space at the top
optional int32 top_mils = 1;
@@ -227,6 +254,8 @@
}
message PrintAttributesProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Media to use
optional ResolutionProto media_size = 1;
@@ -270,8 +299,10 @@
}
message PrintDocumentInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Name of the document to print
- optional string name = 1;
+ optional string name = 1 [ (android.privacy).dest = DEST_EXPLICIT ];
// Number of pages in the doc
optional int32 page_count = 2;
@@ -284,6 +315,8 @@
}
message PageRangeProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Start of the range
optional int32 start = 1;
@@ -292,8 +325,10 @@
}
message PrintJobInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Label of the job
- optional string label = 1;
+ optional string label = 1 [ (android.privacy).dest = DEST_EXPLICIT ];
// Id of the job
optional string print_job_id = 2;
@@ -359,6 +394,8 @@
}
message CachedPrintJobProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// The id of the app the job belongs to
optional int32 app_id = 1;
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
new file mode 100644
index 0000000..b60c569
--- /dev/null
+++ b/core/proto/android/service/usb.proto
@@ -0,0 +1,342 @@
+/*
+ * 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.service.usb;
+
+option java_multiple_files = true;
+option java_outer_classname = "UsbServiceProto";
+
+import "frameworks/base/core/proto/android/content/component_name.proto";
+import "frameworks/base/core/proto/android/service/enums.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+message UsbServiceDumpProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbDeviceManagerProto device_manager = 1;
+ optional UsbHostManagerProto host_manager = 2;
+ optional UsbPortManagerProto port_manager = 3;
+ optional UsbAlsaManagerProto alsa_manager = 4;
+ optional UsbSettingsManagerProto settings_manager = 5;
+}
+
+message UsbDeviceManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbHandlerProto handler = 1;
+ optional UsbDebuggingManagerProto debugging_manager = 2;
+}
+
+message UsbHandlerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ /* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */
+ enum Function {
+ FUNCTION_ADB = 1;
+ FUNCTION_ACCESSORY = 2;
+ FUNCTION_MTP = 4;
+ FUNCTION_MIDI = 8;
+ FUNCTION_PTP = 16;
+ FUNCTION_RNDIS = 32;
+ FUNCTION_AUDIO_SOURCE = 64;
+ }
+
+ repeated Function current_functions = 1;
+ optional bool current_functions_applied = 2;
+ repeated Function screen_unlocked_functions = 3;
+ optional bool screen_locked = 4;
+ optional bool connected = 5;
+ optional bool configured = 6;
+ optional UsbAccessoryProto current_accessory = 7;
+ optional bool host_connected = 8;
+ optional bool source_power = 9;
+ optional bool sink_power = 10;
+ optional bool usb_charging = 11;
+ optional bool hide_usb_notification = 12;
+ optional bool audio_accessory_connected = 13;
+ optional bool adb_enabled = 14;
+ optional string kernel_state = 15;
+ optional string kernel_function_list = 16;
+}
+
+message UsbAccessoryProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string manufacturer = 1;
+ optional string model = 2;
+ optional string description = 3;
+ optional string version = 4;
+ optional string uri = 5;
+ optional string serial = 6 [ (android.privacy).dest = DEST_EXPLICIT ];
+}
+
+message UsbDebuggingManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional bool connected_to_adb = 1;
+ optional string last_key_recevied = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ optional string user_keys = 3 [ (android.privacy).dest = DEST_LOCAL ];
+ optional string system_keys = 4 [ (android.privacy).dest = DEST_LOCAL ];
+}
+
+message UsbHostManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional android.content.ComponentNameProto default_usb_host_connection_handler = 1;
+ repeated UsbDeviceProto devices = 2;
+ optional int32 num_connects = 3;
+ repeated UsbConnectionRecordProto connections = 4;
+}
+
+message UsbDeviceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string name = 1;
+ optional int32 vendor_id = 2;
+ optional int32 product_id = 3;
+ optional int32 class = 4;
+ optional int32 subclass = 5;
+ optional int32 protocol = 6;
+ optional string manufacturer_name = 7;
+ optional string product_name = 8;
+ optional string version = 9;
+ optional string serial_number = 10 [ (android.privacy).dest = DEST_EXPLICIT ];
+ repeated UsbConfigurationProto configurations = 11;
+}
+
+message UsbConfigurationProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 id = 1;
+ optional string name = 2;
+ optional uint32 attributes = 3;
+ optional int32 max_power = 4;
+ repeated UsbInterfaceProto interfaces = 5;
+}
+
+message UsbInterfaceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 id = 1;
+ optional int32 alternate_settings = 2;
+ optional string name = 3;
+ optional int32 class = 4;
+ optional int32 subclass = 5;
+ optional int32 protocol = 6;
+ repeated UsbEndPointProto endpoints = 7;
+}
+
+message UsbEndPointProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 endpoint_number = 1;
+ optional android.service.UsbEndPointDirection direction = 2;
+ optional int32 address = 3;
+ optional android.service.UsbEndPointType type = 4;
+ optional uint32 attributes = 5;
+ optional int32 max_packet_size = 6;
+ optional int32 interval = 7;
+}
+
+message UsbConnectionRecordProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string device_address = 1;
+ optional android.service.UsbConnectionRecordMode mode = 2;
+ optional int64 timestamp = 3;
+ optional int32 manufacturer = 4;
+ optional int32 product = 5;
+ optional UsbIsHeadsetProto is_headset = 6;
+}
+
+message UsbIsHeadsetProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional bool in = 1;
+ optional bool out = 2;
+}
+
+message UsbPortManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional bool is_simulation_active = 1;
+ repeated UsbPortInfoProto usb_ports = 2;
+}
+
+message UsbPortInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbPortProto port = 1;
+ optional UsbPortStatusProto status = 2;
+ optional bool can_change_mode = 3;
+ optional bool can_change_power_role = 4;
+ optional bool can_change_data_role = 5;
+}
+
+message UsbPortProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ /* Same as android.hardware.usb.V1_1.Constants.PortMode_1_1 */
+ enum Mode {
+ MODE_NONE = 0;
+ MODE_UFP = 1;
+ MODE_DFP = 2;
+ MODE_DRP = 3;
+ MODE_AUDIO_ACCESSORY = 4;
+ MODE_DEBUG_ACCESSORY = 8;
+ }
+
+ optional string id = 1;
+ repeated Mode supported_modes = 2;
+}
+
+message UsbPortStatusProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ /* Same as android.hardware.usb.V1_0.Constants.PortPowerRole */
+ enum PowerRole {
+ POWER_ROLE_NONE = 0;
+ POWER_ROLE_SOURCE = 1;
+ POWER_ROLE_SINK = 2;
+ }
+
+ /* Same as android.hardware.usb.V1_0.Constants.PortDataRole */
+ enum DataRole {
+ DATA_ROLE_NONE = 0;
+ DATA_ROLE_HOST = 1;
+ DATA_ROLE_DEVICE = 2;
+ }
+
+ optional bool connected = 1;
+ optional UsbPortProto.Mode current_mode = 2;
+ optional PowerRole power_role = 3;
+ optional DataRole data_role = 4;
+ repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
+}
+
+message UsbPortStatusRoleCombinationProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbPortStatusProto.PowerRole power_role = 1;
+ optional UsbPortStatusProto.DataRole data_role = 2;
+}
+
+message UsbAlsaManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 cards_parser = 1;
+ repeated UsbAlsaDeviceProto alsa_devices = 2;
+ repeated UsbMidiDeviceProto midi_devices = 3;
+}
+
+message UsbAlsaDeviceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 card = 1;
+ optional int32 device = 2;
+ optional string name = 3;
+ optional bool has_playback = 4;
+ optional bool has_capture = 5;
+ optional string address = 6;
+}
+
+message UsbMidiDeviceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 card = 1;
+ optional int32 device = 2;
+ optional string device_address = 3;
+}
+
+message UsbSettingsManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated UsbUserSettingsManagerProto user_settings = 1;
+ repeated UsbProfileGroupSettingsManagerProto profile_group_settings = 2;
+}
+
+message UsbUserSettingsManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 user_id = 1;
+ repeated UsbSettingsDevicePermissionProto device_permissions = 2;
+ repeated UsbSettingsAccessoryPermissionProto accessory_permissions = 3;
+}
+
+message UsbSettingsDevicePermissionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string device_name = 1;
+ repeated int32 uids = 2;
+}
+
+message UsbSettingsAccessoryPermissionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string accessory_description = 1;
+ repeated int32 uids = 2;
+}
+
+message UsbProfileGroupSettingsManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 parent_user_id = 1;
+ repeated UsbSettingsDevicePreferenceProto device_preferences = 2;
+ repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3;
+}
+
+message UsbSettingsDevicePreferenceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbDeviceFilterProto filter = 1;
+ optional UserPackageProto user_package = 2;
+}
+
+message UsbDeviceFilterProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 vendor_id = 1;
+ optional int32 product_id = 2;
+ optional int32 class = 3;
+ optional int32 subclass = 4;
+ optional int32 protocol = 5;
+ optional string manufacturer_name = 6;
+ optional string product_name = 7;
+ optional string serial_number = 8 [ (android.privacy).dest = DEST_EXPLICIT ];
+}
+
+message UserPackageProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 user_id = 1;
+ optional string package_name =2;
+}
+
+message UsbSettingsAccessoryPreferenceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbAccessoryFilterProto filter = 1;
+ optional UserPackageProto user_package = 2;
+}
+
+message UsbAccessoryFilterProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string manufacturer = 1;
+ optional string model = 2;
+ optional string version = 3;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e5ba6d7..deefddb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -323,6 +323,7 @@
<protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
<protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
<protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
+ <protected-broadcast android:name="com.android.server.net.action.SNOOZE_RAPID" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
@@ -1785,13 +1786,31 @@
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
- <p>Protection level: signature|privileged
+ <p>Protection level: signature|privileged|vendorPrivileged
@SystemApi
@hide
-->
<permission android:name="android.permission.BIND_IMS_SERVICE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Must be required by a telephony data service to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a NetworkService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
EuiccManager APIs.
<p>Protection level: signature|privileged|development
@@ -3029,6 +3048,13 @@
<permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
android:protectionLevel="signature|privileged|development" />
+ <!-- Allows an application to collect ambient light stats.
+ <p>Not for use by third party applications.</p>
+ TODO: Make a system API
+ @hide -->
+ <permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS"
+ android:protectionLevel="signature|privileged|development" />
+
<!-- Allows an application to modify the display brightness configuration
@hide
@SystemApi -->
@@ -3835,6 +3861,7 @@
android:excludeFromRecents="true"
android:label="@string/user_owner_label"
android:exported="true"
+ android:visibleToInstantApps="true"
>
</activity>
<activity-alias android:name="com.android.internal.app.ForwardIntentToParent"
@@ -4163,6 +4190,16 @@
<service android:name="com.android.server.display.BrightnessIdleJob"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+
+ <service
+ android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:exported="true">
+ <meta-data
+ android:name="android.accessibilityservice"
+ android:resource="@xml/autofill_compat_accessibility_service" />
+ </service>
+
</application>
</manifest>
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index a6dee8a..f5bbadc 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
-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,36 +13,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <group>
- <path
- android:fillColor="#2C292A"
- android:fillType="evenOdd"
- android:pathData="M6,26a20,20 0 0,1 40,0a20,20 0 0,1 -40,0z"/>
- <path
- android:fillColor="#FAFAFA"
- android:fillType="evenOdd"
- android:pathData="M4,24a20,20 0 0,1 40,0a20,20 0 0,1 -40,0z"/>
- <path
- android:fillColor="#2C292A"
- android:fillType="evenOdd"
- android:pathData="M2,22a20,20 0 0,1 40,0a20,20 0 0,1 -40,0z"/>
- <path
- android:fillColor="#00000000"
- android:strokeColor="#453F41"
- android:strokeWidth="1"
- android:fillType="evenOdd"
- android:pathData="M26.5 29.5v3c0 1.13-.87 2-2 2s-2-.87-2-2v-3h-1v3c0 1.13-.87 2-2 2s-2-.87-2-2v-3H17a1.5 1.5 0 0 1-1.5-1.5V17.5h13V28a1.5 1.5 0 0 1-1.5 1.5h-.5zM13.5 17.5c1.13 0 2 .87 2 2v7c0 1.13-.87 2-2 2s-2-.87-2-2v-7c0-1.13.87-2 2-2zM30.5 17.5c1.13 0 2 .87 2 2v7c0 1.13-.87 2-2 2s-2-.87-2-2v-7c0-1.13.87-2 2-2zM26.3 12.11A6.46 6.46 0 0 1 28.5 17v.5h-13V17a6.46 6.46 0 0 1 2.2-4.89l-.9-.9a.98.98 0 0 1 0-1.41.98.98 0 0 1 1.4 0l1.26 1.25A6.33 6.33 0 0 1 22 10.5c.87 0 1.73.2 2.54.55L25.8 9.8a.98.98 0 0 1 1.4 0 .98.98 0 0 1 0 1.4l-.9.91z"/>
- <path
- android:fillColor="#453F41"
- android:fillType="evenOdd"
- android:pathData="M20.16 14.5a.66.66 0 1 1-1.31 0c0-.36.29-.65.65-.65.36 0 .65.29.65.65zM25.16 14.5c0 .36-.3.66-.66.66a.65.65 0 1 1 .66-.66z"/>
- <path
- android:fillColor="#453F41"
- android:pathData="M22 40.5c0.36 0 0.73-0.01 1.09-0.03l-0.18-3A15.77 15.77 0 0 1 22 37.5v3zm2.17-0.13a18.48 18.48 0 0 0 1.08-0.15l-0.53-2.96c-0.3 0.05-0.6 0.1-0.9 0.13l0.35 2.98zM26.32 40a18.37 18.37 0 0 0 1.05-0.28l-0.87-2.87a15.37 15.37 0 0 1-0.88 0.23l0.7 2.92zm2.1-0.64l-1.03-2.81a15.39 15.39 0 0 0 0.84-0.34l1.2 2.74a18.39 18.39 0 0 1-1 0.41zm1.99-0.87l-1.37-2.67a15.46 15.46 0 0 0 0.8-0.44l1.52 2.59a18.46 18.46 0 0 1-0.95 0.52zm1.89-1.11l-1.67-2.5a15.55 15.55 0 0 0 0.74-0.52l1.81 2.39a18.55 18.55 0 0 1-0.88 0.63zm1.75-1.33l-1.95-2.28a15.6 15.6 0 0 0 0.67-0.61l2.09 2.15a18.6 18.6 0 0 1-0.8 0.74zm1.6-1.55l-2.22-2.02a15.6 15.6 0 0 0 0.6-0.7l2.33 1.9a18.6 18.6 0 0 1-0.72 0.82zM37 32.82l-2.43-1.76a15.53 15.53 0 0 0 0.5-0.75l2.54 1.6c-0.2 0.31-0.4 0.61-0.61 0.9zm1.15-1.8l-2.62-1.47a15.45 15.45 0 0 0 0.42-0.8l2.7 1.3a18.45 18.45 0 0 1-0.5 0.97zm0.95-1.98l-2.77-1.14a15.38 15.38 0 0 0 0.32-0.86l2.84 0.98a18.38 18.38 0 0 1-0.39 1.02zm0.72-2.09c0.1-0.34 0.18-0.7 0.26-1.05l-2.93-0.63a15.38 15.38 0 0 1-0.22 0.88l2.89 0.8zm0.46-2.15a18.52 18.52 0 0 0 0.13-1.08l-2.99-0.28a15.52 15.52 0 0 1-0.1 0.9l2.96 0.46zm0.2-2.2a18.81 18.81 0 0 0 0-1.1l-3 0.08a16 16 0 0 1 0 0.92l3 0.1zm-0.06-2.2a18.54 18.54 0 0 0-0.12-1.07l-2.97 0.43c0.04 0.3 0.08 0.6 0.1 0.9l3-0.25zm-0.31-2.15a18.39 18.39 0 0 0-0.25-1.06l-2.9 0.78a15.39 15.39 0 0 1 0.21 0.89l2.94-0.6zm-0.57-2.12l-2.85 0.95a15.37 15.37 0 0 0-0.31-0.85l2.78-1.12a18.37 18.37 0 0 1 0.38 1.02zm-0.83-2.06l-2.71 1.29a15.44 15.44 0 0 0-0.42-0.81l2.63-1.45a18.44 18.44 0 0 1 0.5 0.97zm-1.03-1.88l-2.54 1.6a15.53 15.53 0 0 0-0.5-0.76l2.44-1.74 0.6 0.9zm-1.28-1.79l-2.33 1.88a15.6 15.6 0 0 0-0.6-0.69l2.23-2.02a18.6 18.6 0 0 1 0.7 0.83zm-1.48-1.63l-2.1 2.14a15.6 15.6 0 0 0-0.67-0.62l1.97-2.26a18.6 18.6 0 0 1 0.8 0.74zM33.24 7.3l-1.82 2.38a15.55 15.55 0 0 0-0.74-0.53l1.68-2.49c0.3 0.2 0.6 0.42 0.88 0.64zm-1.71-1.17L29.98 8.7a15.47 15.47 0 0 0-0.8-0.45l1.4-2.66a18.47 18.47 0 0 1 0.95 0.54zm-1.95-1.02l-1.23 2.74A15.4 15.4 0 0 0 27.5 7.5l1.06-2.8a18.4 18.4 0 0 1 1.01 0.4zm-2.06-0.78l-0.9 2.86a15.37 15.37 0 0 0-0.87-0.24l0.72-2.92a18.37 18.37 0 0 1 1.05 0.3zM25.38 3.8a18.47 18.47 0 0 0-1.08-0.17l-0.37 2.98c0.3 0.04 0.6 0.08 0.9 0.14l0.55-2.95zm-2.2-0.27A18.75 18.75 0 0 0 22.1 3.5l-0.02 3L23 6.53l0.19-3zM21 3.53a18.6 18.6 0 0 0-1.08 0.09l0.33 2.98a15.6 15.6 0 0 1 0.91-0.08l-0.16-3zm-2.16 0.24A18.4 18.4 0 0 0 17.76 4l0.68 2.92a15.4 15.4 0 0 1 0.9-0.18l-0.51-2.96zm-2.14 0.5l0.86 2.88a15.37 15.37 0 0 0-0.86 0.28l-1.03-2.81a18.37 18.37 0 0 1 1.03-0.35zm-2.07 0.76l1.2 2.75a15.42 15.42 0 0 0-0.83 0.4L13.63 5.5a18.42 18.42 0 0 1 0.99-0.47zM12.7 6l1.5 2.6a15.5 15.5 0 0 0-0.76 0.48l-1.66-2.5A18.5 18.5 0 0 1 12.7 6zm-1.83 1.22l1.8 2.4a15.58 15.58 0 0 0-0.7 0.57L10.01 7.9a18.58 18.58 0 0 1 0.85-0.68zM9.19 8.66l2.07 2.16a15.6 15.6 0 0 0-0.63 0.65l-2.2-2.04a18.6 18.6 0 0 1 0.76-0.77zm-1.51 1.63l2.32 1.9a15.57 15.57 0 0 0-0.56 0.72l-2.42-1.76a18.57 18.57 0 0 1 0.66-0.86zm-1.23 1.69l2.52 1.62a15.5 15.5 0 0 0-0.47 0.78l-2.61-1.47a18.5 18.5 0 0 1 0.56-0.93zm-1.08 1.9l2.7 1.32a15.41 15.41 0 0 0-0.38 0.83l-2.77-1.15a18.41 18.41 0 0 1 0.45-1zm-0.85 2.04l2.84 0.98a15.37 15.37 0 0 0-0.28 0.87L4.2 16.96c0.1-0.35 0.2-0.7 0.32-1.04zm-0.6 2.12a18.43 18.43 0 0 0-0.2 1.07l2.97 0.47c0.05-0.3 0.1-0.6 0.17-0.9l-2.93-0.64zm-0.34 2.18a18.65 18.65 0 0 0-0.07 1.09l3 0.11 0.06-0.91-2.99-0.29zm-0.08 2.2a18.7 18.7 0 0 0 0.06 1.1l3-0.25a15.7 15.7 0 0 1-0.06-0.91l-3 0.07zm0.18 2.18a18.44 18.44 0 0 0 0.18 1.07l2.95-0.6a15.44 15.44 0 0 1-0.16-0.9L3.68 24.6zm0.43 2.14l2.9-0.77a15.37 15.37 0 0 0 0.26 0.88l-2.85 0.94a18.37 18.37 0 0 1-0.3-1.05zm0.7 2.1l2.78-1.11a15.4 15.4 0 0 0 0.36 0.83l-2.71 1.27a18.4 18.4 0 0 1-0.44-1zm0.9 1.95l2.65-1.43a15.48 15.48 0 0 0 0.45 0.8l-2.55 1.57a18.48 18.48 0 0 1-0.54-0.94zm1.17 1.87l2.45-1.73a15.56 15.56 0 0 0 0.54 0.73l-2.34 1.87a18.56 18.56 0 0 1-0.65-0.87zm1.37 1.72l2.23-2a15.6 15.6 0 0 0 0.63 0.65l-2.1 2.14a18.6 18.6 0 0 1-0.76-0.79zm1.58 1.56l1.98-2.26c0.22 0.2 0.46 0.39 0.7 0.58l-1.84 2.37a18.59 18.59 0 0 1-0.84-0.7zm1.66 1.28l1.7-2.46a15.52 15.52 0 0 0 0.77 0.5l-1.56 2.56a18.52 18.52 0 0 1-0.91-0.6zm1.87 1.14l1.4-2.65a15.43 15.43 0 0 0 0.82 0.4l-1.24 2.73a18.43 18.43 0 0 1-0.98-0.48zm2 0.91l1.08-2.8a15.37 15.37 0 0 0 0.86 0.3l-0.9 2.86a18.37 18.37 0 0 1-1.04-0.36zm2.1 0.67a18.4 18.4 0 0 0 1.07 0.23l0.56-2.94a15.4 15.4 0 0 1-0.9-0.2l-0.72 2.91zm2.18 0.41a18.57 18.57 0 0 0 1.08 0.1l0.2-2.99a15.57 15.57 0 0 1-0.9-0.09l-0.38 2.98zm2.2 0.15H22v-3h-0.13l-0.03 3z"/>
- </group>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="vector"
+ android:width="640dp"
+ android:height="640dp"
+ android:viewportWidth="64"
+ android:viewportHeight="64">
+ <path
+ android:name="bg"
+ android:pathData="M 27 43 L 32 43 C 38.075 43 43 38.075 43 32 C 43 25.925 38.075 21 32 21 C 25.925 21 21 25.925 21 32 L 21 64"
+ android:strokeColor="#6823a1"
+ android:strokeWidth="16"/>
+ <path
+ android:name="fg"
+ android:pathData="M 29 43 L 32 43 C 38.075 43 43 38.075 43 32 C 43 25.925 38.075 21 32 21 C 25.925 21 21 25.925 21 32 L 21 64"
+ android:strokeColor="#ff0000"
+ android:strokeWidth="8"/>
</vector>
diff --git a/core/res/res/drawable-nodpi/platlogo_m.xml b/core/res/res/drawable-nodpi/platlogo_m.xml
index aacf674..8e43638 100644
--- a/core/res/res/drawable-nodpi/platlogo_m.xml
+++ b/core/res/res/drawable-nodpi/platlogo_m.xml
@@ -18,23 +18,4 @@
android:height="480dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
- <!--<path
- android:pathData="M25.0,25.0m-20.5,0.0a20.5,20.5,0,1,1,41.0,0.0a20.5,20.5,0,1,1,-41.0,0.0"
- android:fillAlpha="0.066"
- android:fillColor="#000000"/>-->
- <path
- android:pathData="M24.0,24.0m-20.0,0.0a20.0,20.0,0,1,1,40.0,0.0a20.0,20.0,0,1,1,-40.0,0.0"
- android:fillColor="#FFC107"/>
- <path
- android:pathData="M44,24.2010101 L33.9004889,14.101499 L14.101499,33.9004889 L24.2010101,44 C29.2525804,43.9497929 34.2887564,41.9975027 38.1431296,38.1431296 C41.9975027,34.2887564 43.9497929,29.2525804 44,24.2010101 Z"
- android:fillColor="#FE9F00"/>
- <path
- android:pathData="M24.0,24.0m-14.0,0.0a14.0,14.0,0,1,1,28.0,0.0a14.0,14.0,0,1,1,-28.0,0.0"
- android:fillColor="#FED44F"/>
- <path
- android:pathData="M37.7829445,26.469236 L29.6578482,18.3441397 L18.3441397,29.6578482 L26.469236,37.7829445 C29.1911841,37.2979273 31.7972024,36.0037754 33.9004889,33.9004889 C36.0037754,31.7972024 37.2979273,29.1911841 37.7829445,26.469236 Z"
- android:fillColor="#FFC107"/>
- <path
- android:pathData="M24.0,24.0m-8.0,0.0a8.0,8.0,0,1,1,16.0,0.0a8.0,8.0,0,1,1,-16.0,0.0"
- android:fillColor="#FFFFFF"/>
</vector>
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 2e2b395..0fde2cc 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
-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,24 +13,24 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <group>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="vector"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <group android:name="stat_sys_adb">
<path
- android:fillColor="#FFFFFF"
- android:fillAlpha=".33"
- android:fillType="evenOdd"
- android:pathData="M5.71 18.29A8.99 8.99 0 0 0 22 13c0-3-1.46-5.65-3.71-7.29A8.99 8.99 0 0 0 2 11c0 3 1.46 5.65 3.71 7.29z"/>
+ android:name="outer"
+ android:pathData="M 18 30 L 24 30 C 29.523 30 34 25.523 34 20 C 34 14.477 29.523 10 24 10 C 18.477 10 14 14.477 14 20 L 14 44"
+ android:strokeColor="#000000"
+ android:strokeWidth="10"/>
<path
- android:fillColor="#FFFFFF"
- android:fillType="evenOdd"
- android:pathData="M7.25 19.18A8.5 8.5 0 0 0 19.19 7.24 9 9 0 0 1 7.24 19.19z"/>
- <path
- android:fillColor="#FFFFFF"
- android:fillAlpha=".33"
- android:pathData="M10.5 3a0.5 0.5 0 1 1 1 0v2.05a0.5 0.5 0 1 1-1 0V3zm3.1 0.42a0.5 0.5 0 0 1 0.93 0.39l-0.8 1.88A0.5 0.5 0 1 1 12.8 5.3l0.8-1.88zm2.7 1.57a0.5 0.5 0 1 1 0.71 0.7l-1.45 1.46a0.5 0.5 0 0 1-0.7-0.71l1.44-1.45zm1.9 2.5a0.5 0.5 0 0 1 0.38 0.92l-1.9 0.77a0.5 0.5 0 0 1-0.37-0.93l1.9-0.77zM19 10.5a0.5 0.5 0 1 1 0 1h-2.05a0.5 0.5 0 0 1 0-1H19zm-0.42 3.1a0.5 0.5 0 0 1-0.39 0.93l-1.88-0.8a0.5 0.5 0 1 1 0.39-0.92l1.88 0.8zm-1.57 2.7a0.5 0.5 0 1 1-0.7 0.71l-1.46-1.45a0.5 0.5 0 0 1 0.71-0.7l1.45 1.44zm-2.5 1.9a0.5 0.5 0 1 1-0.92 0.38l-0.77-1.9a0.5 0.5 0 0 1 0.93-0.37l0.77 1.9zM11.5 19a0.5 0.5 0 1 1-1 0v-2.05a0.5 0.5 0 0 1 1 0V19zm-3.1-0.42a0.5 0.5 0 0 1-0.93-0.39l0.8-1.88A0.5 0.5 0 0 1 9.2 16.7l-0.8 1.88zm-2.7-1.57a0.5 0.5 0 1 1-0.71-0.7l1.45-1.46a0.5 0.5 0 0 1 0.7 0.71L5.7 17.01zm-1.9-2.48a0.5 0.5 0 0 1-0.38-0.92l1.88-0.8a0.5 0.5 0 0 1 0.4 0.92l-1.9 0.8zM3 11.5a0.5 0.5 0 1 1 0-1h2.05a0.5 0.5 0 1 1 0 1H3zm0.42-3.1A0.5 0.5 0 0 1 3.8 7.46l1.88 0.8A0.5 0.5 0 1 1 5.3 9.2L3.42 8.4zm1.57-2.7a0.5 0.5 0 1 1 0.7-0.71l1.46 1.45a0.5 0.5 0 0 1-0.71 0.7L4.99 5.7zm2.5-1.9A0.5 0.5 0 0 1 8.4 3.41l0.77 1.9a0.5 0.5 0 0 1-0.93 0.37L7.48 3.8z"/>
+ android:name="inner"
+ android:pathData="M 19 30 L 24 30 C 29.523 30 34 25.523 34 20 C 34 14.477 29.523 10 24 10 C 18.477 10 14 14.477 14 20 L 14 44"
+ android:strokeColor="#000000"
+ android:strokeAlpha="0"
+ android:strokeWidth="6"/>
</group>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 4ac308a..bd1030e 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -28,9 +28,9 @@
android:scaleType="centerCrop"
android:importantForAccessibility="no" />
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/message_group_and_sender_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:orientation="vertical">
<com.android.internal.widget.ImageFloatingTextView
android:id="@+id/message_name"
@@ -44,4 +44,10 @@
android:spacing="2dp"
android:layout_weight="1"/>
</com.android.internal.widget.RemeasuringLinearLayout>
+ <FrameLayout
+ android:id="@+id/messaging_group_icon_container"
+ android:layout_width="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:layout_marginStart="12dp"
+ android:visibility="gone"/>
</com.android.internal.widget.MessagingGroup>
diff --git a/core/res/res/layout/notification_template_messaging_message.xml b/core/res/res/layout/notification_template_messaging_image_message.xml
similarity index 71%
copy from core/res/res/layout/notification_template_messaging_message.xml
copy to core/res/res/layout/notification_template_messaging_image_message.xml
index ab6466c..6ca4dcd 100644
--- a/core/res/res/layout/notification_template_messaging_message.xml
+++ b/core/res/res/layout/notification_template_messaging_image_message.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ 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,8 +14,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.internal.widget.MessagingMessage
+<com.android.internal.widget.MessagingImageMessage
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/message_text"
- style="@style/Widget.Material.Notification.MessagingText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/messaging_image_extra_spacing"
+ android:scaleType="fitStart"
/>
diff --git a/core/res/res/layout/notification_template_messaging_message.xml b/core/res/res/layout/notification_template_messaging_text_message.xml
similarity index 94%
rename from core/res/res/layout/notification_template_messaging_message.xml
rename to core/res/res/layout/notification_template_messaging_text_message.xml
index ab6466c..e728e69 100644
--- a/core/res/res/layout/notification_template_messaging_message.xml
+++ b/core/res/res/layout/notification_template_messaging_text_message.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.internal.widget.MessagingMessage
+<com.android.internal.widget.MessagingTextMessage
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/message_text"
style="@style/Widget.Material.Notification.MessagingText"
diff --git a/core/res/res/layout/unlaunchable_app_activity.xml b/core/res/res/layout/unlaunchable_app_activity.xml
deleted file mode 100644
index 429d5ed..0000000
--- a/core/res/res/layout/unlaunchable_app_activity.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/dialog_padding"
- android:orientation="vertical">
- <TextView android:id="@+id/unlaunchable_app_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_padding"
- android:paddingBottom="@dimen/dialog_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Title" />
-
- <TextView android:id="@+id/unlaunchable_app_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/textColorSecondary" />
-</LinearLayout>
-
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f3db598..ab54ad8 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Veiligmodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-stelsel"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Skakel oor na persoonlike profiel"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Skakel oor na werkprofiel"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakte"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"in te gaan by jou kontakte"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou kontakte"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"roeteer oproepe deur die stelsel"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"gaan voort met \'n oproep uit \'n ander program"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Laat die program toe om \'n oproep voort te sit wat in \'n ander program begin is."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lees foonnommers"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Laat die program toe om toegang tot die toestel se foonnommers te kry."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"verhoed dat tablet slaap"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan nie gestoor word nie. Verwyder asseblief \'n bestaande vingerafdruk."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Vingerafdrukhandeling is gekanselleer."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Vingerafdrukhandeling is deur gebruiker gekanselleer."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Te veel pogings. Probeer later weer."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Te veel pogings. Vingerafdruksensor is gedeaktiveer."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Probeer weer."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Maak oop"</string>
<string name="sms" msgid="4560537514610063430">"SMS"</string>
<string name="add_contact" msgid="7867066569670597203">"Voeg by"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Bekyk"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Skeduleer"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Spoor na"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Oop Wi-Fi-netwerk beskikbaar</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Koppel aan oop Wi-Fi-netwerk"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Koppel aan diensverskaffer se Wi‑Fi-netwerk"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Koppel tans aan oop Wi‑Fi-netwerk"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Aan Wi-Fi-netwerk gekoppel"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Kon nie aan Wi-Fi-netwerk koppel nie"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Geen toestemmings benodig nie"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dit kan jou dalk geld kos"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB laai tans hierdie toestel"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB verskaf tans krag aan gekoppelde toestel"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB vir lêeroordrag"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB vir foto-oordrag"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB vir MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Gekoppel aan \'n USB-toebehoorsel"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tik vir meer opsies."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analoë oudiobykomstigheid bespeur"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Die aangehegde toestel is nie met hierdie foon versoenbaar nie. Tik om meer te wete te kom."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ontfouter gekoppel"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Werk-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2e werk-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3e werk-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Raak en hou die Terug- en Oorsig-knoppie om hierdie skerm te ontspeld"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Skerm vasgespeld"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Skerm ontspeld"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Vra PIN voordat jy ontspeld"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Vra ontsluitpatroon voordat jy ontspeld"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Vra wagwoord voordat jy ontspeld"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Alle tale"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Allle streke"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Soek"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Skakel werkprofiel aan?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Jou werkprogramme, kennisgewings, data en ander werkprofielkenmerke sal aangeskakel word"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Skakel aan"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Hierdie program is vir \'n ouer weergawe van Android gebou en sal dalk nie behoorlik werk nie. Probeer kyk vir opdaterings, of kontak die ontwikkelaar."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Kyk vir opdatering"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Jy het nuwe boodskappe"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Maak SMS-program oop om te bekyk"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Sommige funksies kan beperk wees"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Foon nie toegelaat vir stem nie"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Opspringvenster"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Hierdie kortpad vereis die jongste program"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Kon nie die kortpad teruglaai nie omdat die program nie rugsteun en teruglaai steun nie"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Kon nie teruglaai nie omdat programondertekening nie ooreenstem nie"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kon nie kortpad teruglaai nie"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Kortpad is gedeaktiveer"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DEΪNSTALLEER"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"MAAK TOG OOP"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Skadelike program is bespeur"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wil <xliff:g id="APP_2">%2$s</xliff:g>-skyfies wys"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Wysig"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0dbda7f..54b913e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>፣ <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"የሚያስተማምን ሁነታ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ስርዓት"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"ወደ የግል መገለጫ ቀይር"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"ወደ የስራ መገለጫ ቀይር"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ዕውቂያዎች"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"የእርስዎ እውቂያዎች ላይ ይድረሱባቸው"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> እውቂያዎችዎን እንዲደርስ ይፍቀዱለት"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"መተግበሪያው የመሳሪያውን የስልክ ባህሪያት ላይ እንዲደርስ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው የስልክ ቁጥሩን እና የመሳሪያውን መታወቂያዎች፣ ጥሪ የነቃ እንደሆነ፣ እና በጥሪ የተገናኘውን የሩቅ ቁጥር እንዲወስን ይፈቅድለታል።"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ጥሪዎችን በስርዓቱ በኩል አዙር"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"መተግበሪያው የጥሪ ተሞክሮን እንዲያሻሽል ጥሪዎቹን በስርዓቱ በኩል እንዲያዞር ያስችለዋል።"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"በሌላ መተግበሪያ የተጀመረ ጥሪን መቀጠል"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"መተግበሪያው በሌላ መተግበሪያ ውስጥ የተጀመረ ጥሪ እንዲቀጥል ያስችለዋል።"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ስልክ ቁጥሮች ያንብቡ"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"መተግበሪያው የመሣሪያውን የስልክ ቁጥሮች እንዲደርስባቸው ይፈቅድለታል።"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ጡባዊ ከማንቀላፋት ተከላከል"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"የጣት አሻራ ሊከማች አይችልም። እባክዎ አሁን ያለውን የጣት አሻራ ያስወግዱ።"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"የጣት አሻራ ስርዓተ ክወና ተትቷል።"</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"በጣም ብዙ ሙከራዎች። የጣት አሻራ ዳሳሽ ተሰናክሏል።"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"እንደገና ይሞክሩ።"</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"ክፈት"</string>
<string name="sms" msgid="4560537514610063430">"መልዕክት"</string>
<string name="add_contact" msgid="7867066569670597203">"አክል"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"ይመልከቱ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"መርሐግብር"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ተከታተል"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">የሚገኙ የWi-Fi አውታረ መረቦችን ክፈት</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"ከክፍት የWi‑Fi አውታረ መረብ ጋር ያገናኙ"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"ከአገልግሎት አቅራቢ Wi-Fi አውታረ መረብ ጋር ይገናኙ"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"ከክፍት የWi‑Fi አውታረ መረብ ጋር በመገናኘት ላይ"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"ከWi‑Fi አውታረ መረብ ጋር ተገናኝቷል"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ከWi‑Fi አውታረ መረብ ጋር መገናኘት አልተቻለም"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"ምንም ፍቃዶች አይጠየቁም"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ይህ ገንዘብ ሊያስወጣዎት ይችላል"</string>
<string name="dlg_ok" msgid="7376953167039865701">"እሺ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"ዩኤስቢ የዚህን መሣሪያ ኃይል በመሙላት ላይ"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ዩኤስቢ ለተያያዘው መሣሪያ ኃይል በማቅረብ ላይ"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ዩኤስቢ ለፋይል ሽግግር"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ዩኤስቢ ለፎቶ ሽግግር"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"ዩኤስቢ ለMIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ለUSB ተቀጥላ ተያይዟል"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"ለተጨማሪ አማራጮች መታ ያድርጉ።"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"የአናሎግ ኦዲዮ ረዳት እንዳለ ተደርሶበታል"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"ዓባሪ የተያያዘው መሣሪያ ከዚህ ስልክ ጋር ተኳዃኝ አይደለም። የበለጠ ለመረዳት መታ ያድርጉ።"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB አድስ ተያይዟል"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2ኛ ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3ኛ ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ይህን ማያ ገጽ ለመንቀል ተመለስ እና አጠቃላይ ዕይታን ተጭነው ይያዙ"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"ማያ ገጽ ተሰክቷል"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"ማያ ገጽ ተነቅሏል"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ከመንቀል በፊት ፒን ጠይቅ"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"ከመንቀል በፊት የማስከፈቻ ስርዓተ-ጥለት ጠይቅ"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"ከመንቀል በፊት የይለፍ ቃል ጠይቅ"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"ሁሉም ቋንቋዎች"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"ሁሉም ክልሎች"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"ፈልግ"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"የስራ መገለጫ ይብራ?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"የእርስዎ የስራ መተግበሪያዎች፣ ማሳወቂያዎች፣ ውሂብ እና ሌሎች የስራ መገለጫ ባህሪያት ይበራሉ"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"አብራ"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ይህ መተግበሪያ ለቆየ የAndroid ስሪት ነው የተገነባው፣ እና በአግባቡ ላይሰራ ይችላል። ዝማኔዎች ካሉ ለመመልከት ይሞክሩ፣ ወይም ደግሞ ገንቢውን ያነጋግሩ።"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"ዝማኔ ካለ አረጋግጥ"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"አዲስ መልእክቶች አለዎት"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"ለመመልከት የኤስኤምኤስ መተግበሪያ ይክፈቱ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"አንዳንድ ተግባሮች የተገደቡ ሊሆኑ ይችላሉ"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"ስልክ ለድምጽ አይፈቀድም"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ብቅ-ባይ መስኮት"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ይህ አቋራጭ በጣም የቅርብ ጊዜውን መተግበሪያ ይፈልጋል"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"መተግበሪያ ምትኬን እና ወደ ነበረበት መመለስን ሳለማይደግፍ አቋራጭ ወደ ነበረበት ሊመለስ አልቻለም"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"በመተግበሪያ ፊርማ አለመዛመድ አቋራጭን ወደነበረበት መመለስ አልተቻለም"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"አቋራጭን ወደ ነበረበት መመለስ አልተቻለም"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"አቋራጭ ተሰናክሏል"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"አራግፍ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ለማንኛውም ክፈት"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ጎጂ መተግበሪያ ተገኝቷል"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን ማሳየት ይፈልጋል"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"አርትዕ"</string>
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 5255a12..5254fcd 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tətbiqə cihazın telefon funksiyalarına giriş icazəsi verir. Belə icazəli tətbiq bu telefonun nömrəsini və cihaz İD\'ni, zəngin aktiv olub-olmadığını və zəng edilən nömrəni müəyyən edə bilər."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"zəngləri sistem üzərindən yönləndirin"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tətbiqə, zəng təcrübəsini yaxşılaşdırmaq üçün, zəngləri sistem üzərindən yönləndirməyə icazə verilir."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"zəngə digər tətbiqdən davam edin"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Tətbiqə digər tətbiqdə başlayan zəngə davam etmək icazəsi verilir."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon nömrələrini oxuyun"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Tətbiqə cihazın telefon nömrələrinə daxil olmağa icazə verir."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"planşetin yuxu rejiminin qarşısını almaq"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Cəhdlər çox oldu. Sonraya saxlayın."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Həddindən çox cəhd. Barmaq izi sensoru deaktiv edilib."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Yenidən cəhd edin."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Açın"</string>
<string name="sms" msgid="4560537514610063430">"Mesaj"</string>
<string name="add_contact" msgid="7867066569670597203">"Əlavə edin"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Baxın"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Cədvəl"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Trek"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Yaddaş yeri bitir"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bəzi sistem funksiyaları işləməyə bilər"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Əlçatan açıq Wi-Fi şəbəkəsi</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Açıq Wi‑Fi şəbəkəsinə qoşulun"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Operatorun Wi‑Fi şəbəkəsinə qoşulun"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Açıq Wi‑Fi şəbəkəsinə qoşulur"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Wi‑Fi şəbəkəsinə qoşuldu"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi‑Fi şəbəkəsinə qoşulmaq mümkün deyil"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Heç bir icazə tələb olunmur"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"bununla sizdən xərc tutula bilər"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB bu cihaza enerji doldurur"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Qolulmuş cihaz üçün USB enerji tədarükü"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"Fayl transferi üçün USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"Foto transfer üçün USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI üçün USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB aksesuara qoşuldu"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Əlavə seçimlər üçün tıklayın."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analoq audio aksesuar aşkarlandı"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Əlavə edilən cihaz bu telefonla uyğun deyil. Ətraflı məlumat üçün klikləyin."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB sazlama qoşuludur"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"İş <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2-ci İş <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3-cü İş <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Bu ekrandan sancağı götürmək üçün Geri və İcmal düymələrinə basıb saxlayın"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ayırmadan öncə PIN istənilsin"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Ayırmadan öncə kilid modeli istənilsin"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Ayırmadan öncə parol istənilsin"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"İş profili aktiv edilsin?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"İş tətbiqləri, bildirişləri, data və digər iş profili funksiyaları aktiv ediləcək"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Aktivləşdirin"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Bu tətbiq köhnə Android versiyası üçün hazırlanıb və düzgün işləməyə bilər. Güncəlləməni yoxlayın və ya developer ilə əlaqə saxlayın."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Güncəlləməni yoxlayın"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Yeni mesajlarınız var"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Baxmaq üçün SMS tətbiqini açın"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Bir neçə funksionallıq məhdudlaşdırıla bilər"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Səs üçün telefona icazə verilmir"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Popap Pəncərəsi"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Bu qısayol ən son tətbiqi tələb edir"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Qısayolu bərpa etmək mümkün olmadı, çünki tətbiq yedəkləməni və bərpa etməyi dəstəkləmir"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Tətbiqin imza uyğunsuzluğu səbəbilə qısayolu bərpa etmək mümkün olmadı"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Qısayolu bərpa etmək mümkün olmadı"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Qısayol deaktiv edildi"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"SİSTEMDƏN SİLİN"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"İSTƏNİLƏN HALDA AÇIN"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Zərərli tətbiq aşkarlandı"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> tətbiqindən bölmələr göstərmək istəyir"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Redaktə edin"</string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index ecadbbf..21cf7d6 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"নিরাপদ মোড"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android সিস্টেম"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"এর বদলে ব্যক্তিগত প্রোফাইল ব্যবহার করুন"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"এর বদলে কাজের প্রোফাইল ব্যবহার করুন"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"পরিচিতি"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"আপনার পরিচিতিগুলিতে অ্যাক্সেস"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> কে আপনার পরিচিতিগুলি অ্যাক্সেস করতে দিন"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"অ্যাপ্লিকেশানটিকে ডিভাইসের ফোন বৈশিষ্ট্যগুলিকে অ্যাক্সেস করার অনুমতি দেয়৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে একটি কল সক্রিয় থাকা অবস্থায় এবং দূরবর্তী নম্বর একটি কল দ্বারা সংযুক্ত থাকাকালীনও ফোন নম্বর এবং ডিভাইসের IDগুলি নির্ধারণ করার অনুমতি দেয়৷"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"সিস্টেমের মাধ্যমে কলগুলি রুট করতে দিন"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"কল করার অভিজ্ঞতা উন্নত করার জন্য অ্যাপকে সিস্টেমের মাধ্যমে তার কলগুলি রুট করতে দেয়।"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"অন্য কোনও অ্যাপ দিয়ে করে থাকা কল চালিয়ে যান"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"অন্য কোনও অ্যাপ দিয়ে কল করলে এই অ্যাপটিকে সেটি চালিয়ে যেতে দেয়।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ফোন নম্বরগুলি পড়া হোক"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"অ্যাপটিকে এই ডিভাইসের ফোন নম্বরগুলি অ্যাক্সেস করতে দেয়।"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ঘুমানো থেকে ট্যাবলেটকে প্রতিরোধ করে"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"আঙ্গুলের ছাপ সংরক্ষণ করা যাবে না৷ অনুগ্রহ করে একটি বিদ্যমান আঙ্গুলের ছাপ সরান৷"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"আঙ্গুলের ছাপ অপারেশন বাতিল করা হয়েছে৷"</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ব্যবহারকারী আঙ্গুলের ছাপের অপারেশনটি বাতিল করেছেন।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"অনেকবার প্রচেষ্টা করা হয়েছে৷ পরে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"বহুবার চেষ্টা করেছেন। আঙ্গুলের ছাপ নেওয়ার সেন্সর অক্ষম করা হয়েছে।"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"আবার চেষ্টা করুন৷"</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"খুলুন"</string>
<string name="sms" msgid="4560537514610063430">"মেসেজ"</string>
<string name="add_contact" msgid="7867066569670597203">"যোগ করুন"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"দেখুন"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"সময়সূচী"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ট্র্যাক"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">খোলা ওয়াই-ফাই নেটওয়ার্কগুলি উপলব্ধ রয়েছে</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"উন্মুক্ত ওয়াই-ফাই নেটওয়ার্কে সংযোগ করুন"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"পরিষেবা প্রদানকারীর ওয়াই-ফাই নেটওয়ার্কে সংযোগ করুন"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"উন্মুক্ত ওয়াই-ফাই নেটওয়ার্কে সংযোগ করা হচ্ছে"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"উন্মুক্ত ওয়াই-ফাই নেটওয়ার্কে সংযুক্ত করা হয়েছে"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ওয়াই-ফাই নেটওয়ার্কে সংযোগ করা গেল না"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"কোনো অনুমতির প্রয়োজন নেই"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"এর জন্য অর্থপ্রদান করতে হতে পারে"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ঠিক আছে"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"এই ডিভাইসটি USB দ্বারা চার্জ করা হচ্ছে"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"সংযুক্ত ডিভাইসটিতে USB পাওয়ার সরবরাহ করছে"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ফাইল স্থানান্তরের জন্য USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ফটো স্থানান্তরের জন্য USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI এর জন্য USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"একটি USB যন্ত্রাংশতে সংযুক্ত হয়েছে"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"আরও বিকল্পের জন্য আলতো চাপুন৷"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"অ্যানালগ অডিও অ্যাক্সেসরি শনাক্ত করা হয়েছে"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"সংযুক্ত ডিভাইসটি এই ফোনের সাথে ব্যবহার করা যাবে না। আরও জানতে ট্যাপ করুন।"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ডিবাগিং সংযুক্ত হয়েছে"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"কর্মক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"দ্বিতীয় কার্যক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"তৃতীয় কার্যক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"এই স্ক্রিনটিকে আনপিন করতে ফিরে যাওয়া এবং এক নজরে বোতামদুটি ট্যাপ করে ধরে রাখুন"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"স্ক্রিন পিন করা হয়েছে"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"পিন না করা স্ক্রীন"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"আনপিন করার আগে পিন চান"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"আনপিন করার আগে আনলক প্যাটার্ন চান"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"আনপিন করার আগে পাসওয়ার্ড চান"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"সকল ভাষা"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"সমস্ত অঞ্চল"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"খুঁজুন"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"কাজের প্রোফাইল চালু করবেন?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"আপনার কাজের অ্যাপ, বিজ্ঞপ্তি, ডেটা এবং কাজের প্রোফাইলের অন্যান্য বৈশিষ্ট্য চালু করা হবে"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"চালু করুন"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"এই অ্যাপটি Android এর একটি পুরনো ভার্সনের জন্য তৈরি করা হয়েছিল, তাই এখানে সেটি ঠিকমতো কাজ নাও করতে পারে। আপডেট পাওয়া যাচ্ছে কিনা দেখুন বা ডেভেলপারের সাথে যোগাযোগ করুন।"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"আপডেট পাওয়া যাচ্ছে কিনা দেখুন"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"আপনার নতুন মেসেজ আছে"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"দেখার জন্য SMS অ্যাপ্লিকেশান খুলুন"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"কিছু ক্রিয়াকলাপ সীমিত হতে পারে"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"এই ফোন দিয়ে ভয়েস কল করা যাবে না"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"পপ-আপ উইন্ডো"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>টি"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"এই শর্টকাটটির জন্য লেটেস্ট অ্যাপ প্রয়োজন"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"শর্টকাট ফিরিয়ে আনা যায়নি কারণ অ্যাপটিতে \'ব্যাক-আপ এবং রিস্টোর\' করার সুবিধা নেই"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"শর্টকাট ফিরিয়ে আনা যায়নি কারণ অ্যাপের সিগ্নেচারটি মিল হচ্ছে না"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"শর্টকাট ফিরিয়ে আনা যায়নি"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"শর্টকাট অক্ষম করা আছে"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"আন-ইনস্টল করুন"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"যাই হোক, খুলতে চাই"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ক্ষতিকর অ্যাপ শনাক্ত করা হয়েছে"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> অ্যাপটি <xliff:g id="APP_2">%2$s</xliff:g> এর অংশ দেখাতে চায়"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"এডিট করুন"</string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f143d4e..6eef37c 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -267,10 +267,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistem"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Pređite na lični profil"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Pređite na radni profil"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakti"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"pristupa vašim kontaktima"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Dozvolite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup vašim kontaktima"</string>
@@ -422,10 +420,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Omogućava aplikaciji pristup telefonskim funkcijama uređaja. Ovo odobrenje omogućava aplikaciji određivanje telefonskog i identifikacionog broja uređaja, bez obzira da li je poziv aktivan i da li je uspostavljena veza sa pozivanim brojem."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"usmjeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Dopušta aplikaciji da pozive usmjeri preko sistema radi poboljšanja iskustva pozivanja."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"nastavlja poziv iz druge aplikacije"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Dozvoljava aplikaciji nastavljanje poziva koji je započet u drugoj aplikaciji."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"čitanje telefonskih brojeva"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Dozvoljava aplikaciji pristup telefonskim brojevima uređaja."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"sprečavanje tableta da uđe u režim mirovanja"</string>
@@ -500,11 +496,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta se ne može pohraniti. Uklonite postojeći otisak prsta."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vremensko ograničenje za otisak prsta je isteklo. Pokušajte ponovo."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Radnja sa otiskom prsta je otkazana."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Korisnik je otkazao radnju s otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Previše pokušaja. Senzor za otisak prsta je onemogućen."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Pokušajte ponovo."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -567,8 +566,8 @@
<string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Dozvoljava vlasniku povezivanje s interfejsom najvišeg nivoa u servisu za poruke operatera. Nije potrebno za obične aplikacije."</string>
<string name="permlab_bindCarrierServices" msgid="3233108656245526783">"povezivanje na usluge operatera"</string>
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Omogućava vlasniku povezivanje sa uslugama operatera. Obično nije potrebno za obične aplikacije."</string>
- <string name="permlab_access_notification_policy" msgid="4247510821662059671">"pristup opciji Ne ometaj"</string>
- <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Omogućava aplikaciji da čita i upisuje konfiguraciju opcije Ne ometaj."</string>
+ <string name="permlab_access_notification_policy" msgid="4247510821662059671">"pristup načinu rada Ne ometaj"</string>
+ <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Omogućava aplikaciji da čita i upisuje konfiguraciju načina rada Ne ometaj."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Postavljanje pravila za lozinke"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN-ovima."</string>
<string name="policylab_watchLogin" msgid="5091404125971980158">"Prati pokušaje otključavanja ekrana"</string>
@@ -1020,12 +1019,9 @@
<string name="browse" msgid="1245903488306147205">"Otvorite"</string>
<string name="sms" msgid="4560537514610063430">"Poruka"</string>
<string name="add_contact" msgid="7867066569670597203">"Dodajte"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Prikaži"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Zakaži"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Prati"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1155,8 +1151,7 @@
<item quantity="other">Otvorene Wi-Fi mreže su dostupne</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Povežite se na otvorenu Wi‑Fi mrežu"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Povežite se na Wi‑Fi mrežu mobilnog operatera"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Povezivanje na otvorenu Wi‑Fi mrežu"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Povezani ste na Wi‑Fi mrežu"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Nije se moguće povezati na Wi‑Fi mrežu"</string>
@@ -1240,13 +1235,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Nisu potrebna odobrenja"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ovo se možda dodatno plaća"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Uredu"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Punjenje uređaja putem USB-a"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB napaja priključeni uređaj"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB za prijenos fajlova"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB za prijenos slika"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Uspostavljena veza sa USB pohranom"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za više opcija."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Otkriven je analogni periferni uređaj"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Priključeni uređaj nije kompatibilan s ovim telefonom. Dodirnite da saznate više."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka putem uređaja spojenog na USB je uspostavljeno"</string>
@@ -1688,9 +1693,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Da otkačite ovaj ekran, dodirnite i držite dugmad Nazad i Pregled."</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Ekran je zakačen"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran je otkačen"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Traži PIN prije nego se otkači"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Traži uzorak za otključavanje prije nego se otkači"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Traži lozinku prije nego se otkači"</string>
@@ -1744,7 +1746,7 @@
<string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="931849471004038757">"Dok ne isključite"</string>
- <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dok ne isključite opciju Ne ometaj"</string>
+ <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dok ne isključite način rada Ne ometaj"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"Skupi"</string>
<string name="zen_mode_feature_name" msgid="5254089399895895004">"Ne ometaj"</string>
@@ -1793,15 +1795,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Svi jezici"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Sve regije"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Pretraga"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Uključiti radni profil?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Uključit će se poslovne aplikacije, obavještenja, podaci i druge funkcije radnog profila"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Uključi"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Ova aplikacija je pravljena za stariju verziju Androida i možda neće ispravno raditi. Provjerite jesu li dostupna ažuriranja ili kontaktirajte programera."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Provjeri je li dostupno ažuriranje"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Imate nove poruke"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Otvorite SMS aplikaciju da biste pregledali poruke"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Neke funkcije mogu biti ograničene"</string>
@@ -1869,17 +1867,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon nije dozvoljen za govor"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iskočni prozor"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Za ovu prečicu potrebna je najnovija aplikacija"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Prečica nije uspješno vraćena jer aplikacija ne podržava izradu sigurnosne kopije i vraćanje"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Prečica nije uspješno vraćena zbog nepodudaranja potpisa aplikacije"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Prečica nije uspješno vraćena"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Prečica je onemogućena"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DEINSTALIRAJ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"IPAK OTVORI"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Otkrivena je štetna aplikacija"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"Aplikacija <xliff:g id="APP_0">%1$s</xliff:g> želi prikazati isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Uredi"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index bf91550..abdb966 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode segur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Canvia al perfil personal"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Canvia al perfil professional"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactes"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accedir als contactes"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Permet que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi als contactes"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet que l\'aplicació accedeixi a les funcions de telèfon del dispositiu. Aquest permís permet que l\'aplicació determini el número de telèfon i els identificadors del dispositiu, si hi ha una trucada activa i el número remot connectat amb una trucada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminar trucades a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permet que l\'aplicació encamini les trucades a través del sistema per millorar-ne l\'experiència."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"Continua una trucada d\'una altra aplicació"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permet que l\'aplicació continuï una trucada que s\'havia iniciat en una altra aplicació."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"llegir els números de telèfon"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permet que l\'aplicació accedeixi als números de telèfon del dispositiu."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"evita que la tauleta entri en mode de repòs"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"S\'han produït massa intents. Torna-ho a provar més tard."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"S\'han fet massa intents. S\'ha desactivat el sensor d\'empremtes digitals."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Torna-ho a provar."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Obre"</string>
<string name="sms" msgid="4560537514610063430">"Missatge"</string>
<string name="add_contact" msgid="7867066569670597203">"Afegeix"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Mostra"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Programa"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Fes un seguiment"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Xarxa Wi-Fi oberta disponible</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connecta\'t a una xarxa Wi-Fi oberta"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Connecta\'t a la xarxa Wi-Fi de l\'operador de telefonia mòbil"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"S\'està connectant a una xarxa Wi-Fi oberta"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"S\'ha connectat a la xarxa Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"No s\'ha pogut connectar a una xarxa Wi-Fi"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No cal cap permís"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"pot ser que comporti càrrecs"</string>
<string name="dlg_ok" msgid="7376953167039865701">"D\'acord"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"El dispositiu s\'està carregant per USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"L\'USB subministra energia al dispositiu connectat"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB per transferir fitxers"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB per transferir fotos"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per a MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connectat a un accessori USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Toca per veure més opcions."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"S\'ha detectat un accessori d\'àudio analògic"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"El dispositiu connectat no és compatible amb aquest telèfon. Toca per obtenir més informació."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració USB activada"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de la feina"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2n <xliff:g id="LABEL">%1$s</xliff:g> de la feina"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3r <xliff:g id="LABEL">%1$s</xliff:g> de la feina"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Toca i mantén premuts els botons Enrere i Aplicacions recents per deixar de fixar aquesta pantalla"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Pantalla fixada"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Fixació de la pantalla anul·lada"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Sol·licita el codi PIN per deixar de fixar"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Sol·licita el patró de desbloqueig per deixar de fixar"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Demana la contrasenya per deixar de fixar"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Tots els idiomes"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Totes les regions"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Cerca"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Activar el perfil professional?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"S\'activaran les teves aplicacions per a la feina, les notificacions, les dades i altres funcions del perfil professional"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Activa"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Aquesta aplicació es va crear per a una versió antiga d\'Android i pot ser que no funcioni correctament. Prova de cercar actualitzacions o contacta amb el desenvolupador."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Cerca actualitzacions"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Tens missatges nous"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Obre l\'aplicació d\'SMS per veure\'ls"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Algunes funcions es limitaran"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"El telèfon no és compatible per a la veu"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Finestra emergent"</string>
<string name="slice_more_content" msgid="8504342889413274608">"<xliff:g id="NUMBER">%1$d</xliff:g> més"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Per fer servir aquesta drecera has de tenir l\'última versió de l\'aplicació"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"No s\'ha pogut restaurar la drecera perquè l\'aplicació no permet la còpia de seguretat ni la restauració"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"No s\'ha pogut restaurar la drecera perquè la signatura de l\'aplicació no coincideix"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"No s\'ha pogut restaurar la drecera"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"S\'ha desactivat la drecera"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DESINSTAL·LA"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OBRE IGUALMENT"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"S\'ha detectat una aplicació perjudicial"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> vol mostrar porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Edita"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 32d4294..89b6e29 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -270,10 +270,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Přepnout na osobní profil"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Přepnout na pracovní profil"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"přístup ke kontaktům"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Povolte aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup ke kontaktům"</string>
@@ -425,10 +423,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Umožňuje aplikaci získat přístup k telefonním funkcím zařízení. Toto oprávnění umožňuje aplikaci zjistit telefonní číslo telefonu, identifikační čísla zařízení, zda zrovna probíhá hovor, a vzdálené číslo, ke kterému je hovor připojen."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"směrování volání prostřednictvím systému"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Umožňuje aplikaci směrovat volání prostřednictvím systému za účelem vylepšení funkcí volání."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"pokračování v hovoru v jiné aplikaci"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Umožňuje aplikace pokračovat v hovoru, který byl zahájen v jiné aplikaci."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"přístup k telefonním číslům"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Umožňuje aplikaci přístup k telefonním číslům v zařízení."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"bránění přechodu tabletu do režimu spánku"</string>
@@ -503,11 +499,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisk prstu nelze uložit. Odstraňte existující otisk prstu."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit sejmutí otisku prstu vypršel. Zkuste to znovu."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Operace otisku prstu byla zrušena."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Uživatel operaci s otiskem prstu zrušil."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Příliš mnoho pokusů. Zkuste to později."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Příliš mnoho pokusů. Snímač otisků prstů byl deaktivován."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Zkuste to znovu."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1040,12 +1039,9 @@
<string name="browse" msgid="1245903488306147205">"Otevřít"</string>
<string name="sms" msgid="4560537514610063430">"Zpráva"</string>
<string name="add_contact" msgid="7867066569670597203">"Přidat"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Zobrazit"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Naplánovat"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Sledovat"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1175,8 +1171,7 @@
<item quantity="one">K dispozici je veřejná síť Wi-Fi</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Připojení k otevřené síti Wi-Fi"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Připojte se k síti Wi-Fi operátora"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Připojování k otevřené síti Wi-Fi"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Připojeno k síti Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Připojení k síti Wi-Fi se nezdařilo"</string>
@@ -1260,13 +1255,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Nejsou vyžadována žádná oprávnění"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"může vás to něco stát"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Nabíjení zařízení přes USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Připojené zařízení se nabíjí přes USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB na přenos souborů"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB na přenos fotek"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB v režimu MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Připojeno k perifernímu zařízení USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Klepnutím zobrazíte další možnosti."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Bylo zjištěno analogové zvukové příslušenství"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Připojené zařízení není s tímto telefonem kompatibilní. Klepnutím zobrazíte další informace."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ladění přes USB připojeno"</string>
@@ -1711,9 +1716,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Pracovní <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2. <xliff:g id="LABEL">%1$s</xliff:g> do práce"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3. <xliff:g id="LABEL">%1$s</xliff:g> do práce"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Chcete-li tuto obrazovku uvolnit, podržte tlačítka Zpět a Přehled"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Obrazovka připnuta"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Obrazovka uvolněna"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Před uvolněním požádat o PIN"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Před uvolněním požádat o bezpečnostní gesto"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Před uvolněním požádat o heslo"</string>
@@ -1825,15 +1827,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Všechny jazyky"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Všechny oblasti"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Vyhledávání"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Zapnout pracovní profil?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Vaše pracovní aplikace, oznámení, data a ostatní funkce pracovního účtu budou zapnuty"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Zapnout"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Tato aplikace byla vytvořena pro starší verzi systému Android a nemusí fungovat správně. Zkuste vyhledat aktualizace, případně kontaktujte vývojáře."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Zkontrolovat aktualizace"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Máte nové zprávy"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Zobrazíte je v aplikaci pro SMS"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Funkce mohou být omezeny"</string>
@@ -1902,17 +1900,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon není povolen pro hlasovou komunikaci"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Vyskakovací okno"</string>
<string name="slice_more_content" msgid="8504342889413274608">"a ještě <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Tato zkratka vyžaduje nejnovější verzi aplikace"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Zkratku nelze obnovit, protože aplikace nepodporuje zálohování a obnovu"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Zkratku nelze obnovit, protože se neshoduje podpis aplikace"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Zkratku nelze obnovit"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Zkratka nefunguje"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ODINSTALOVAT"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"PŘESTO OTEVŘÍT"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Byla zjištěna škodlivá aplikace"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"Aplikace <xliff:g id="APP_0">%1$s</xliff:g> chce zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Upravit"</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0f774d6..873dd61 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Allows the app to access the phone numbers of the device."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"prevent tablet from sleeping"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Too many attempts. Try again later."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Too many attempts. Fingerprint sensor disabled."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Try again."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Open"</string>
<string name="sms" msgid="4560537514610063430">"Message"</string>
<string name="add_contact" msgid="7867066569670597203">"Add"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"View"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Schedule"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Open Wi-Fi network available</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connect to open Wi‑Fi network"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Connect to operator Wi‑Fi network"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connecting to open Wi‑Fi network"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connected to Wi‑Fi network"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Could not connect to Wi‑Fi network"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analogue audio accessory detected"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"The attached device is not compatible with this phone. Tap to learn more."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Ask for unlock pattern before unpinning"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Ask for password before unpinning"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Turn on work profile?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Your work apps, notifications, data and other work profile features will be turned on"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Turn on"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Check for update"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Open SMS app to view"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Some functionality may be limited"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"UNINSTALL"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OPEN ANYWAY"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Harmful app detected"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Edit"</string>
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 0f774d6..873dd61 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Allows the app to access the phone numbers of the device."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"prevent tablet from sleeping"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Too many attempts. Try again later."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Too many attempts. Fingerprint sensor disabled."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Try again."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Open"</string>
<string name="sms" msgid="4560537514610063430">"Message"</string>
<string name="add_contact" msgid="7867066569670597203">"Add"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"View"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Schedule"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Open Wi-Fi network available</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connect to open Wi‑Fi network"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Connect to operator Wi‑Fi network"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connecting to open Wi‑Fi network"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connected to Wi‑Fi network"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Could not connect to Wi‑Fi network"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analogue audio accessory detected"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"The attached device is not compatible with this phone. Tap to learn more."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Ask for unlock pattern before unpinning"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Ask for password before unpinning"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Turn on work profile?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Your work apps, notifications, data and other work profile features will be turned on"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Turn on"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Check for update"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Open SMS app to view"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Some functionality may be limited"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"UNINSTALL"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OPEN ANYWAY"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Harmful app detected"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Edit"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 0f774d6..873dd61 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Allows the app to access the phone numbers of the device."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"prevent tablet from sleeping"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Too many attempts. Try again later."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Too many attempts. Fingerprint sensor disabled."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Try again."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Open"</string>
<string name="sms" msgid="4560537514610063430">"Message"</string>
<string name="add_contact" msgid="7867066569670597203">"Add"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"View"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Schedule"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Open Wi-Fi network available</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connect to open Wi‑Fi network"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Connect to operator Wi‑Fi network"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connecting to open Wi‑Fi network"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connected to Wi‑Fi network"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Could not connect to Wi‑Fi network"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analogue audio accessory detected"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"The attached device is not compatible with this phone. Tap to learn more."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Ask for unlock pattern before unpinning"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Ask for password before unpinning"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Turn on work profile?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Your work apps, notifications, data and other work profile features will be turned on"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Turn on"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Check for update"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Open SMS app to view"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Some functionality may be limited"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"UNINSTALL"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OPEN ANYWAY"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Harmful app detected"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Edit"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 0f774d6..873dd61 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Allows the app to access the phone numbers of the device."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"prevent tablet from sleeping"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Too many attempts. Try again later."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Too many attempts. Fingerprint sensor disabled."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Try again."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Open"</string>
<string name="sms" msgid="4560537514610063430">"Message"</string>
<string name="add_contact" msgid="7867066569670597203">"Add"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"View"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Schedule"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Open Wi-Fi network available</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connect to open Wi‑Fi network"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Connect to operator Wi‑Fi network"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connecting to open Wi‑Fi network"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connected to Wi‑Fi network"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Could not connect to Wi‑Fi network"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analogue audio accessory detected"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"The attached device is not compatible with this phone. Tap to learn more."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Ask for unlock pattern before unpinning"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Ask for password before unpinning"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Turn on work profile?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Your work apps, notifications, data and other work profile features will be turned on"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Turn on"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Check for update"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Open SMS app to view"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Some functionality may be limited"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"UNINSTALL"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OPEN ANYWAY"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Harmful app detected"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Edit"</string>
</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 8160f0d..85cfb87 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active, and the remote number connected by a call."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continue a call from another app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Allows the app to continue a call which was started in another app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"read phone numbers"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Allows the app to access the phone numbers of the device."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"prevent tablet from sleeping"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Too many attempts. Try again later."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Too many attempts. Fingerprint sensor disabled."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Try again."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Open"</string>
<string name="sms" msgid="4560537514610063430">"Message"</string>
<string name="add_contact" msgid="7867066569670597203">"Add"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"View"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Schedule"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Track"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Open Wi-Fi network available</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connect to open Wi‑Fi network"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Connect to carrier Wi‑Fi network"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connecting to open Wi‑Fi network"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connected to Wi‑Fi network"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Could not connect to Wi‑Fi network"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No permissions required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analog audio accessory detected"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"The attached device is not compatible with this phone. Tap to learn more."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Ask for unlock pattern before unpinning"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Ask for password before unpinning"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Turn on work profile?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Your work apps, notifications, data, and other work profile features will be turned on"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Turn on"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Check for update"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"You have new messages"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Open SMS app to view"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Some functionality may be limited"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Popup Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"UNINSTALL"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OPEN ANYWAY"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Harmful app detected"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wants to show <xliff:g id="APP_2">%2$s</xliff:g> slices"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Edit"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d1a2fa3..487d68a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Cambiar al perfil personal"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Cambiar al perfil de trabajo"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder a tus contactos"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Permite que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a tus contactos"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"direccionar llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite a la aplicación direccionar sus llamadas hacia el sistema para mejorar la calidad de estas."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar una llamada de otra aplicación"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que la aplicación continúe una llamada que se ha iniciado en otra aplicación."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"leer números de teléfono"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permite que la aplicación acceda a los números de teléfono del dispositivo."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"impedir que el tablet entre en modo de suspensión"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una ya creada."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Se ha alcanzado el tiempo de espera de la huella digital. Vuelve a intentarlo."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Se ha cancelado la operación de huella digital."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"El usuario ha cancelado la operación de huella digital."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Demasiados intentos. Se ha inhabilitado el sensor de huellas digitales."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Vuelve a intentarlo."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="sms" msgid="4560537514610063430">"Mensaje"</string>
<string name="add_contact" msgid="7867066569670597203">"Añadir"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Ver"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Programar"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Buscar"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Red Wi-Fi abierta disponible</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Conectarse a una red Wi-Fi abierta"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Conectarse a la red Wi‑Fi de un operador"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Conectándose a una red Wi-Fi abierta"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Conectado a la red Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"No se ha podido conectar a la red Wi-Fi"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"No es necesario ningún permiso"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"es posible que esto te cueste dinero"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Cargando este dispositivo por USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"El dispositivo conectado recibe la alimentación por USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferir archivos"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferir fotos"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Toca para ver más opciones."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Se ha detectado un accesorio de audio analógico"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB habilitada"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de trabajo"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> de trabajo 2"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> de trabajo 3"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Mantén pulsado el botón Atrás y el de aplicaciones recientes para dejar de fijar esta pantalla"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Pantalla fijada"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"La pantalla ya no está fija"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Solicitar PIN para desactivar"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Solicitar patrón de desbloqueo para desactivar"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Solicitar contraseña para desactivar"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Todos los idiomas"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Todas las regiones"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Buscar"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"¿Activar el perfil de trabajo?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Tus aplicaciones, notificaciones, datos y otras funciones del perfil de trabajo se activarán"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Activar"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Esta aplicación se ha diseñado para una versión anterior de Android y es posible que no funcione correctamente. Busca actualizaciones o ponte en contacto con el desarrollador."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Comprobar actualizaciones"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Tienes mensajes nuevos"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Abre la aplicación de SMS para ver el mensaje"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Algunas funciones limitadas"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Teléfono no permitido para voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ventana emergente"</string>
<string name="slice_more_content" msgid="8504342889413274608">"<xliff:g id="NUMBER">%1$d</xliff:g> más"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Para usar este acceso directo, necesitas la última versión de la aplicación"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"No se ha podido restaurar el acceso directo porque la aplicación no es compatible con la función de copia de seguridad y restauración"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"No se ha podido restaurar el acceso directo porque la firma de la aplicación no coincide"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"No se ha podido restaurar el acceso directo"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"El acceso directo está inhabilitado"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DESINSTALAR"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ABRIR IGUALMENTE"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Se ha detectado una aplicación dañina"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> quiere mostrar fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Editar"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e5ed2a3..8d1350a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Vaihda henkilökohtaiseen profiiliin"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Vaihda työprofiiliin"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktit"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"käyttää yhteystietoja"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Anna sovellukselle <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> yhteystietojesi käyttöoikeus."</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Antaa sovelluksen käyttää laitteen puhelinominaisuuksia. Sovellus voi määrittää puhelinnumeron ja laitteen tunnuksen, puhelun tilan sekä soitetun numeron."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ohjata puhelut järjestelmän kautta"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Tämä sallii sovelluksen ohjata puhelut järjestelmän kautta, mikä auttaa parantamaan puhelujen laatua."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"jatkaa toisen sovelluksen puhelua"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Antaa sovelluksen jatkaa puhelua, joka aloitettiin toisessa sovelluksessa."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lukea puhelinnumeroita"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Anna sovelluksen käyttää laitteella olevia puhelinnumeroita."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"estä tablet-laitetta menemästä virransäästötilaan"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sormenjälkeä ei voida tallentaa. Poista aiemmin lisätty sormenjälki."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Sormenjälkitunnistimen toiminta aikakatkaistiin. Yritä uudelleen."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Sormenjälkitoiminto peruutettiin."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Käyttäjä peruutti sormenjälkitoiminnon."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Liian monta yritystä. Sormenjälkitunnistin poistettu käytöstä."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Yritä uudelleen."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Avaa"</string>
<string name="sms" msgid="4560537514610063430">"Viesti"</string>
<string name="add_contact" msgid="7867066569670597203">"Lisää"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Näytä"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Aikataulu"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Seuraa"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Avoin Wi-Fi-verkko käytettävissä</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Yhdistä avoimeen Wi‑Fi-verkkoon"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Yhdistä operaattorin Wi-Fi-verkkoon"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Yhdistetään avoimeen Wi‑Fi-verkkoon"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Yhdistetty Wi-Fi-verkkoon"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi‑Fi-verkkoon yhdistäminen epäonnistui"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Lupia ei tarvita"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"tämä voi maksaa"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Laitetta ladataan USB-yhteyden kautta"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Liitetty laite saa virtaa USB-yhteyden kautta"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB on tiedonsiirtotilassa"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB on kuvansiirtotilassa"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB on MIDI-tilassa"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Liitetty USB-laitteeseen"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Näet lisää vaihtoehtoja napauttamalla."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analoginen äänilaite havaittu"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Liitetty laite ei ole yhteensopiva puhelimen kanssa. Napauta, niin näet lisätietoja."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-vianetsintä yhdistetty"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (työ)"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Toinen <xliff:g id="LABEL">%1$s</xliff:g>, työ"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Kolmas <xliff:g id="LABEL">%1$s</xliff:g>, työ"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Irrota näyttö koskettamalla pitkään Takaisin- ja Viimeisimmät-painikkeita"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Näyttö kiinnitetty"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Näyttö irrotettu"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pyydä PIN ennen irrotusta"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Pyydä lukituksenpoistokuvio ennen irrotusta"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Pyydä salasana ennen irrotusta"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Kaikki kielet"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Kaikki alueet"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Haku"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Otetaanko työprofiili käyttöön?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Työsovellukset, ‑ilmoitukset, ‑tiedot ja muut työprofiiliominaisuudet otetaan käyttöön"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Ota käyttöön"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Tämä sovellus on suunniteltu vanhemmalle Android-versiolle eikä välttämättä toimi oikein. Kokeile tarkistaa päivitykset tai ottaa yhteyttä kehittäjään."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Tarkista päivitykset"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Sinulle on uusia viestejä"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Katso avaamalla tekstiviestisovellus."</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Toimintorajoitus mahdollinen"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Äänipalvelujen käyttö ei sallittua puhelimella"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ponnahdusikkuna"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Tämä pikakuvake edellyttää uusinta sovellusta."</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Pikakuvakkeen palautus epäonnistui, koska sovellus ei tue varmuuskopiointia eikä palauttamista."</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Pikakuvakkeen palautus epäonnistui sovelluksen allekirjoituksen yhteensopimattomuuden vuoksi."</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Pikakuvakkeen palautus epäonnistui."</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Pikakuvake on poistettu käytöstä."</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"POISTA"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"AVAA SILTI"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Haitallinen sovellus havaittu"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> haluaa näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Muokkaa"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index aa6608a..2891b8c 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -181,6 +181,7 @@
<string name="network_logging_notification_text" msgid="7930089249949354026">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Touchez ici pour obtenir plus d\'information."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Le contenu de votre appareil sera effacé"</string>
<string name="factory_reset_message" msgid="7972496262232832457">"Impossible d\'utiliser l\'application d\'administration. Les données de votre appareil vont maintenant être effacées.\n\nSi vous avez des questions, communiquez avec l\'administrateur de votre organisation."</string>
+ <string name="printing_disabled_by" msgid="8936832919072486965">"Impression désactivée par <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
<string name="me" msgid="6545696007631404292">"Moi"</string>
<string name="power_dialog" product="tablet" msgid="8545351420865202853">"Options de la tablette"</string>
<string name="power_dialog" product="tv" msgid="6153888706430556356">"Options du téléviseur"</string>
@@ -263,8 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sans échec"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
- <string name="user_owner_label" msgid="1119010402169916617">"Passer au profil personnel"</string>
- <string name="managed_profile_label" msgid="5289992269827577857">"Passer au profil professionnel"</string>
+ <string name="user_owner_label" msgid="8836124313744349203">"Passer au profil personnel"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Passer au profil professionnel"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accéder à vos contacts"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à vos contacts"</string>
@@ -416,6 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"acheminer les appels dans le système"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permet à l\'application d\'acheminer ses appels dans le système afin d\'améliorer l\'expérience d\'appel."</string>
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuer un appel d\'une autre application"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permet à l\'application de continuer un appel commencé dans une autre application."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lire les numéros de téléphone"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permet à l\'application d\'accéder aux numéros de téléphone de l\'appareil."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"empêcher la tablette de passer en mode veille"</string>
@@ -485,13 +488,19 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
+ <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Doigt non reconnu"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empreinte digitale ne peut pas être enregistrée. Veuillez supprimer une empreinte existante."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Le temps attribué pour lire l\'empreinte est écoulé. Veuillez essayer de nouveau."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Opération d\'empreinte numérique annulée."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Trop de tentatives. Veuillez réessayer plus tard."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Trop de tentatives. Capteur d\'empreintes digitales désactivé."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Réessayer."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -990,6 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Ouvrir"</string>
<string name="sms" msgid="4560537514610063430">"Message"</string>
<string name="add_contact" msgid="7867066569670597203">"Ajouter"</string>
+ <string name="view_calendar" msgid="979609872939597838">"Afficher"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Calendrier"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Effectuer le suivi"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1115,6 +1127,7 @@
<item quantity="other">Réseaux Wi-Fi ouverts à proximité</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Connectez-vous pour ouvrir un réseau Wi-Fi"</string>
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Se connecter au réseau Wi-Fi du fournisseur de services"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connexion en cours au réseau Wi-Fi ouvert…"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connecté au réseau Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Impossible de se connecter au réseau Wi-Fi"</string>
@@ -1198,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Aucune autorisation requise"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"cela peut engendrer des frais"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Cet appareil est en cours de charge par USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Un connecteur USB alimente cet appareil"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB pour le transfert de fichiers"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB pour le transfert de photos"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB pour MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connecté à un accessoire USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Touchez pour afficher plus d\'options."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Un accessoire audio analogique a été détecté"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"L\'appareil connecté n\'est pas compatible avec ce téléphone. Touchez ici en savoir plus."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB activé"</string>
@@ -1412,6 +1435,8 @@
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> au-delà de la limite spécifiée."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Données en arrière-plan limitées"</string>
<string name="data_usage_restricted_body" msgid="469866376337242726">"Touchez pour suppr. restriction."</string>
+ <string name="data_usage_rapid_title" msgid="4579994056245665351">"Consomm. importante de données"</string>
+ <string name="data_usage_rapid_body" msgid="4899922842674185567">"Votre consommation récente de données est plus importante que d\'habitude. Touchez l\'écran pour afficher l\'usage et les paramètres."</string>
<string name="ssl_certificate" msgid="6510040486049237639">"Certificat de sécurité"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Ce certificat est valide."</string>
<string name="issued_to" msgid="454239480274921032">"Émis à :"</string>
@@ -1641,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (travail)"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2e <xliff:g id="LABEL">%1$s</xliff:g> professionnel(le)"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3e <xliff:g id="LABEL">%1$s</xliff:g> professionnel(le)"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Pour annuler l\'épinglage de cet écran, maintenez enfoncés les boutons Retour et Aperçu."</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Écran épinglé"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Épinglage d\'écran annulé"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Demander le NIP avant d\'annuler l\'épinglage"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Demander le schéma de déverrouillage avant d\'annuler l\'épinglage"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Demander le mot de passe avant d\'annuler l\'épinglage"</string>
@@ -1688,7 +1710,7 @@
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="9128205721301330797">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
- <string name="zen_mode_forever" msgid="1916263162129197274">"Jusqu\'à ce que vous désactiviez ce mode"</string>
+ <string name="zen_mode_forever" msgid="931849471004038757">"Jusqu\'à la désactivation"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Jusqu\'à ce que vous désactiviez le mode « Ne pas déranger »"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"Réduire"</string>
@@ -1737,9 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Toutes les langues"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Toutes les régions"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Rechercher"</string>
- <string name="work_mode_off_title" msgid="2615362773958585967">"Activer le mode professionnel?"</string>
- <string name="work_mode_off_message" msgid="2961559609199223594">"Votre profil professionnel sera activé, y compris les applications, la synchronisation en arrière-plan et les fonctionnalités associées"</string>
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Activer le profil professionnel?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Vos applications professionnelles, vos notifications, vos données et les autres fonctionnalités de profil professionnel seront activées"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Activer"</string>
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Cette application a été conçue pour une ancienne version d\'Android et pourrait ne pas fonctionner correctement. Essayez de vérifier les mises à jour ou communiquez avec son concepteur."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Vérifier la présence de mises à jour"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Vous avez de nouveaux messages"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Ouvrez l\'application de messagerie texte pour l\'afficher"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Certaines fonct. sont limitées"</string>
@@ -1806,12 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Ce téléphone n\'est pas autorisé pour la voix"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre contextuelle"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Ce raccourci nécessite la dernière version de l\'application"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Impossible de restaurer le raccourci, car l\'application ne prend pas en charge la sauvegarde et la restauration"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Impossible de restaurer le raccourci en raison d\'une erreur de correspondance des signature d\'applications"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossible de restaurer le raccourci"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Le raccourci est désactivé"</string>
- <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Désinstaller"</string>
- <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lancer quand même"</string>
- <string name="harmful_app_warning_title" msgid="2229996292333310435">"Désinstaller l\'application nuisible?"</string>
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DÉSINSTALLER"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"OUVRIR QUAND MÊME"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Une application nuisible a été détectée"</string>
+ <string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> souhaite afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches"</string>
+ <string name="screenshot_edit" msgid="7867478911006447565">"Modifier"</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 0ed3d6d..9356c96 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"સુરક્ષિત મોડ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android સિસ્ટમ"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"વ્યક્તિગત પ્રોફાઇલ પર સ્વિચ કરો"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"કાર્યાલયની પ્રોફાઇલ પર સ્વિચ કરો"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"સંપર્કો"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"તમારા સંપર્કોને ઍક્સેસ કરવાની"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને તમારા સંપર્કોને ઍક્સેસ કરવાની મંજૂરી આપો"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"એપ્લિકેશનને ફોન સુવિધાઓને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને ફોન નંબર અને ઉપકરણ ID, કૉલ સક્રિય છે અને કોઈ કૉલ દ્વારા કનેક્ટ થયેલ રિમોટ નંબર નિર્ધારિત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"સિસ્ટમ મારફતે કૉલ બીજે વાળો"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"કૉલિંગ અનુભવ સુધારવા માટે ઍપ્લિકેશનને સિસ્ટમ મારફતે કૉલ બીજે વાળવાની મંજૂરી આપે છે."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"તૃતીય પક્ષ ઍપમાંનો કૉલ ચાલુ રાખો"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"એક અન્ય તૃતીય પક્ષ ઍપમાં ચાલુ થયેલા કૉલને આ ઍપમાં ચાલુ રાખવાની મંજૂરી આપે છે."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ફોન નંબર વાંચો"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ઍપ્લિકેશનને ઉપકરણનાં ફોન નંબરને ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ટેબ્લેટને નિષ્ક્રિય થતું અટકાવો"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ફિંગરપ્રિન્ટ સંગ્રહિત કરી શકાતી નથી. કૃપા કરીને અસ્તિત્વમાંની ફિંગરપ્રિન્ટ દૂર કરો."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ફિંગરપ્રિન્ટનો સમય બાહ્ય થયો. ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ઘણા બધા પ્રયત્નો. પછીથી ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ઘણા વધુ પ્રયત્નો. ફિંગરપ્રિન્ટ સેન્સર અક્ષમ કરવામાં આવ્યું છે."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ફરી પ્રયાસ કરો."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"ખોલો"</string>
<string name="sms" msgid="4560537514610063430">"સંદેશ મોકલો"</string>
<string name="add_contact" msgid="7867066569670597203">"ઉમેરો"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"જુઓ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"શેડ્યૂલ કરો"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ટ્રૅક કરો"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"કેટલાક સિસ્ટમ કાર્યો કામ કરી શકશે નહીં"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">ખુલ્લા વાઇ-ફાઇ નેટવર્ક્સ ઉપલબ્ધ છે</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"ખુલ્લા વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કરો"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"કૅરિઅરના વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કરો"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"ખુલ્લા વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કરી રહ્યાં છીએ"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કર્યુ"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"વાઇ-ફાઇ નેટવર્ક સાથે કનેક્ટ કરી શકાયું નથી"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"કોઈ પરવાનગીઓ જરૂરી નથી"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"આનાથી તમારા પૈસા ખર્ચ થઈ શકે છે"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ઓકે"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"આ ઉપકરણને USB થી ચાર્જ કરે છે"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"જોડાયેલ ઉપકરણ માટે USB પાવર પૂરો પાડે છે"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ફાઇલ ટ્રાન્સફર માટે USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ફોટો ટ્રાન્સફર માટે USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI માટે USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ઍક્સેસરીથી કનેક્ટ થયાં"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"વધુ વિકલ્પો માટે ટૅપ કરો."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"એનાલોગ ઑડિઓ ઍક્સેસરી મળી"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"જોડેલ ઉપકરણ આ ફોન સાથે સુસંગત નથી. વધુ જાણવા માટે ટૅપ કરો."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ડીબગિંગ કનેક્ટ થયું."</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"કાર્યાલય <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2જું કાર્ય <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3જું કાર્ય <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"આ સ્ક્રીનને અનપિન કરવા માટે, પાછળ અને ઝલક બટનોને સ્પર્શ કરી રાખો"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"સ્ક્રીન પિન કરી"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"સ્ક્રીન અનપિન કરી"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"અનપિન કરતા પહેલાં પિન માટે પૂછો"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"અનપિન કરતા પહેલાં અનલૉક પૅટર્ન માટે પૂછો"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"અનપિન કરતાં પહેલાં પાસવર્ડ માટે પૂછો"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"બધી ભાષાઓ"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"તમામ પ્રદેશ"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"શોધ"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"કાર્યાલયની પ્રોફાઇલ ચાલુ કરીએ?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"તમારી કાર્યાલયની ઍપ, નોટિફિકેશન, ડેટા અને અન્ય કાર્યાલયની પ્રોફાઇલ સુવિધાઓ ચાલુ કરવામાં આવશે"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ચાલુ કરો"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"આ ઍપ Androidના જૂના વર્ઝન માટે બનાવવામાં આવ્યું હતું અને તે કદાચ તે યોગ્ય રીતે કાર્ય કરી શકશે નહીં. અપડેટ માટે તપાસવાનો પ્રયાસ કરો અથવા ડેવલપરનો સંપર્ક કરો."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"અપડેટ માટે તપાસો"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"તમારી પાસે નવા સંદેશા છે"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"જોવા માટે SMS ઍપ્લિકેશન ખોલો"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"કેટલીક કાર્યક્ષમતા મર્યાદિત હોઈ શકે છે"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"વૉઇસ માટે આ ફોનને મંજૂરી નથી"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"પૉપઅપ વિંડો"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"આ શૉર્ટકટ માટે નવી ઍપ જરૂરી છે"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"શૉર્ટકટ ફરી મેળવી શકાયો નથી કારણ કે ઍપ બૅકઅપ અને ફરી મેળવવાનું સમર્થન કરતી નથી"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"શૉર્ટકટ ફરી મેળવી શકાયો નથી કારણ કે ઍપમાં છે તે સહી મેળ ખાતી નથી"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"શૉર્ટકટ પાછો મેળવી શકાયો નથી"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"શૉર્ટકટને બંધ કરવામાં આવ્યો છે"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"અનઇન્સ્ટૉલ કરો"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"કોઈપણ રીતે ખોલો"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"નુકસાનકારક ઍપ મળી આવી છે"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g>એ <xliff:g id="APP_2">%2$s</xliff:g> સ્લાઇસ બતાવવા માગે છે"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"ફેરફાર કરો"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d9f592c..9dc0e2f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android सिस्टम"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"प्रोफ़ाइल बदलकर निजी प्रोफ़ाइल पर जाएं"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"प्रोफ़ाइल बदलकर कार्य प्रोफ़ाइल पर जाएं"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"संपर्क"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"अपने संपर्कों को ऐक्सेस करने की"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने संपर्क तक पहुंचने दें"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ऐप्स को डिवाइस की फ़ोन सुविधाओं तक पहुंचने देता है. यह अनुमति ऐप्स को फ़ोन नंबर और डिवाइस आईडी, कॉल सक्रिय है या नहीं, और कॉल द्वारा कनेक्ट किया गया दूरस्थ नंबर निर्धारित करने देती है."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"सिस्टम के माध्यम से कॉल रूट करें"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कॉल करने के अनुभव को बेहतर बनाने के लिए ऐप्लिकेशन को सिस्टम के माध्यम से उसके कॉल रूट करने देती है."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"दूसरे ऐप्लिकेशन से शुरू किया गया कॉल जारी रखें"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"इसके ज़रिए आप, किसी ऐप्लिकेशन में शुरू किया गया कॉल दूसरे ऐप्लिकेशन में जारी रख सकते हैं."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फ़ोन नंबर पढ़ना"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ऐप को डिवाइस के फ़ोन नंबर का इस्तेमाल करने देती है."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"टैबलेट को सोने (कम बैटरी मोड) से रोकें"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"फ़िंगरप्रिंट क्रियान्वयन रोक दिया गया."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"बहुत अधिक प्रयास कर लिए गए हैं. बाद में पुन: प्रयास करें."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"बहुत अधिक कोशिशें. फ़िंगरप्रिंट सेंसर अक्षम है."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"पुन: प्रयास करें."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"उंगली <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"खोलें"</string>
<string name="sms" msgid="4560537514610063430">"मैसेज"</string>
<string name="add_contact" msgid="7867066569670597203">"जोड़ें"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"देखें"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"शेड्यूल करें"</string>
+ <string name="view_flight" msgid="7691640491425680214">"मौजूदा स्थिति जानें"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"मेमोरी में जगह नहीं बची है"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">खुले वाई-फ़ाई नेटवर्क उपलब्ध</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"खुले वाई-फ़ाई नेटवर्क से कनेक्ट करें"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी के वाई-फ़ाई नेटवर्क से कनेक्ट करें"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"खुले वाई-फ़ाई नेटवर्क से कनेक्ट हो रहा है"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"वाई-फ़ाई नेटवर्क से कनेक्ट हो गया है"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"वाई-फ़ाई नेटवर्क से कनेक्ट नहीं हो सका"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"किसी अनुमति की आवश्यकता नहीं है"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"इससे आपको धन देना पड़ सकता है"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ठीक है"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"यह डिवाइस USB से चार्ज हो रहा है"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"अटैच किए गए डिवाइस को USB से पावर मिल रही है"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"फ़ाइल स्थानांतरण के लिए USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"फ़ोटो स्थानांतरण के लिए USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI के लिए USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB सहायक सामग्री से कनेक्ट किया गया"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"ज़्यादा विकल्पों के लिए टैप करें."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"एनालॉग ऑडियो एक्सेसरी का पता चला"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"अटैच किया गया डिवाइस इस फ़ोन से संगत नहीं है. अधिक जानने के लिए टैप करें."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग कनेक्ट किया गया"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्यस्थल का <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"दूसरा कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"तीसरा कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"इस स्क्रीन को अनपिन करने के लिए, \'वापस जाएं\' और \'खास जानकारी\' के बटन को दबाकर रखें"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"स्क्रीन पिन की गई"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"स्क्रीन अनपिन की गई"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"अनपिन करने से पहले पिन के लिए पूछें"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"अनपिन करने से पहले लॉक खोलने के पैटर्न के लिए पूछें"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"अनपिन करने से पहले पासवर्ड के लिए पूछें"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"सभी भाषाएं"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सभी क्षेत्र"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"सर्च करें"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"कार्य प्रोफ़ाइल चालू करें?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"आपके काम से जुड़े ऐप्लिकेशन, सूचनाएं, डेटा और कार्य प्रोफ़ाइल से जुड़ी दूसरी सुविधाएं चालू हो जाएंगी"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"चालू करें"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"यह ऐप्लिकेशन Android के पुराने वर्शन के लिए बनाया गया था, इसलिए हो सकता है कि यह सही से काम न करे. देखें कि अपडेट मौजूद हैं या नहीं, या फिर डेवलपर से संपर्क करें."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"देखें कि अपडेट मौजूद है या नहीं"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"आपके पास नए संदेश हैं"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"देखने के लिए मैसेज (एसएमएस) ऐप खोलें"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"कुछ कार्य क्षमताएं सीमित हो सकती हैं"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"फ़ोन से कॉल करने की इजाज़त नहीं है"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"इस शॉर्टकट वाला ऐप चलाने के लिए इसका नया वर्शन डाउनलोड करें"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"शॉर्टकट बहाल नहीं किया जा सका क्योंकि इस ऐप में बैकअप लेने और उसे बहाल करने की सुविधा नहीं है"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ऐप सिग्नेचर अलग होने के कारण शॉर्टकट बहाल नहीं किया जा सका"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट बहाल नहीं किया जा सका"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद है"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"अनइंस्टॉल करें"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"फिर भी खोलें"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"नुकसान पहुंचाने वाले ऐप का पता चला"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाना चाहता है"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"बदलाव करें"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8807282..e0df831 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Consente all\'applicazione di accedere alle funzioni telefoniche del dispositivo. Questa autorizzazione consente all\'applicazione di determinare il numero di telefono e gli ID dei dispositivi, se una chiamata è attiva e il numero remoto connesso da una chiamata."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"indirizzamento delle chiamate tramite il sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Consente all\'app di indirizzare le proprie chiamate tramite il sistema al fine di migliorare l\'esperienza di chiamata."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"Continuazione di una chiamata da un\'altra app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Consente all\'app di continuare una chiamata che è stata iniziata in un\'altra app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"lettura dei numeri di telefono"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Consente all\'app di accedere ai numeri di telefono del dispositivo."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"disattivazione stand-by del tablet"</string>
@@ -495,10 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossibile memorizzare l\'impronta digitale. Rimuovi un\'impronta esistente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timeout impronta digitale. Riprova."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Operazione associata all\'impronta digitale annullata."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Operazione associata all\'impronta digitale annullata dall\'utente."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Operazione di autenticazione dell\'impronta digitale annullata dall\'utente."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Troppi tentativi. Riprova più tardi."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Troppi tentativi. Sensore di impronte digitali disattivato."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Riprova."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Apri"</string>
<string name="sms" msgid="4560537514610063430">"Invia messaggio"</string>
<string name="add_contact" msgid="7867066569670597203">"Aggiungi"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Visualizza"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Pianifica"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Monitora"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Apri rete Wi-Fi disponibile</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Stabilisci la connessione per aprire la rete Wi‑Fi"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Collegati alla rete Wi-Fi dell\'operatore"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Connessione per aprire la rete Wi‑Fi"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Connessione alla rete Wi-Fi stabilita"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Impossibile connettersi alla rete Wi-Fi"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Nessuna autorizzazione richiesta"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"potrebbe comportare dei costi"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Dispositivo in carica tramite USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Dispositivo collegato alimentato tramite USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB per il trasferimento di file"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB per il trasferimento di foto"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per la modalità MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Collegato a un accessorio USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Tocca per altre opzioni."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Accessorio audio analogico rilevato"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Il dispositivo collegato non è compatibile con questo telefono. Tocca per avere ulteriori informazioni."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Debug USB collegato"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> lavoro"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> di lavoro (2°)"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> di lavoro (3°)"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Per sganciare la schermata, tieni premuti i pulsanti Indietro e Panoramica"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Schermata fissata"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Schermata sganciata"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Richiedi il PIN per lo sblocco"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Richiedi sequenza di sblocco prima di sbloccare"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Richiedi password prima di sbloccare"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Attivare il profilo di lavoro?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Le tue app di lavoro, le notifiche, i dati e altri elementi del profilo di lavoro saranno attivati."</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Attiva"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Questa app è stata realizzata per una versione precedente di Android e potrebbe non funzionare correttamente. Prova a verificare la disponibilità di aggiornamenti o contatta lo sviluppatore."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Verifica la presenza di aggiornamenti"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Hai nuovi messaggi"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Apri l\'app SMS per la visualizzazione"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Funzioni potenzial. limitate"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefono non consentito per la voce"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Finestra popup"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Per questa scorciatoia è necessaria l\'app più recente"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Impossibile ripristinare la scorciatoia perché l\'app non supporta il backup e il ripristino"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Impossibile ripristinare la scorciatoia perché la firma dell\'app non corrisponde"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossibile ripristinare la scorciatoia"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"La scorciatoia è disattivata"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DISINSTALLA"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"APRI COMUNQUE"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"App dannosa rilevata"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"L\'app <xliff:g id="APP_0">%1$s</xliff:g> vuole mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Modifica"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index ee690b5..14c295f 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"ಸುರಕ್ಷಿತ ಮೋಡ್"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ಸಿಸ್ಟಂ"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"ವೈಯಕ್ತಿಕ ಪ್ರೊಫೈಲ್ಗೆ ಬದಲಾಯಿಸಿ"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ಗೆ ಬದಲಿಸಿ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ಸಂಪರ್ಕಗಳು"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಪ್ರವೇಶಿಸಲು"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಿ"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ಸಾಧನದ ಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ಫೋನ್ ಸಂಖ್ಯೆ ಮತ್ತು ಸಾಧನದ ID ಗಳನ್ನು ನಿರ್ಧರಿಸಲು, ಕರೆಯು ಸಕ್ರಿಯವಾಗಿದೆಯೇ ಮತ್ತು ಕರೆಯ ಮೂಲಕ ರಿಮೋಟ್ ಸಂಖ್ಯೆಯು ಸಂಪರ್ಕಗೊಂಡಿವೆಯೇ ಎಂಬುದನ್ನೂ ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸುತ್ತದೆ."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಿ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ಕರೆಯ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ನ ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಲು ಅನುಮತಿಸುತ್ತದೆ."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"ಮತ್ತೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಮೂಲಕ ಕರೆಯನ್ನು ಮುಂದುವರಿಸಿ"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ಮತ್ತೊಂದು ಅಪ್ಲಿಕೇಶನ್ನಲ್ಲಿ ಪ್ರಾರಂಭವಾದ ಕರೆಯನ್ನು ಮುಂದುವರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡಿ."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ಫೋನ್ ಸಂಖ್ಯೆಗಳನ್ನು ಓದಿ"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ಸಾಧನದ ಫೋನ್ ಸಂಖ್ಯೆಗಳಿಗೆ ಪ್ರವೇಶ ಪಡೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ಟ್ಯಾಬ್ಲೆಟ್ ನಿದ್ರಾವಸ್ಥೆಯನ್ನು ತಡೆಯಿರಿ"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಬೆರಳಚ್ಚು ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಬೆರಳಚ್ಚು ತೆಗೆದುಹಾಕಿ."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ಬೆರಳಚ್ಚು ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"ಬೆರಳಚ್ಚು ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ಬಳಕೆದಾರರಿಂದ ಫಿಂಗರ್ ಫ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಲಾಗಿದೆ."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ಹಲವಾರು ಪ್ರಯತ್ನಗಳು. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಫಿಂಗರ್ ಫ್ರಿಂಟ್ ಸೆನ್ಸಾರ್ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"ತೆರೆ"</string>
<string name="sms" msgid="4560537514610063430">"ಸಂದೇಶ"</string>
<string name="add_contact" msgid="7867066569670597203">"ಸೇರಿಸಿ"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"ವೀಕ್ಷಿಸಿ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ಅವಧಿ"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ಟ್ರ್ಯಾಕ್"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">ಮುಕ್ತ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗಳು ಲಭ್ಯವಿವೆ</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"ಮುಕ್ತ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಿಸಿ"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"ವಾಹಕ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಿಸಿ"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"ಮುಕ್ತ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"ಯಾವುದೇ ಅನುಮತಿಗಳ ಅಗತ್ಯವಿಲ್ಲ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ಇದು ನಿಮ್ಮ ಹಣವನ್ನು ವ್ಯಯಿಸಬಹುದು"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ಸರಿ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"ಈ ಸಾಧನಕ್ಕೆ USB ಅನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB, ಲಗತ್ತಿಸಲಾದ ಸಾಧನಕ್ಕೆ ಪವರ್ ಪೂರೈಸುತ್ತಿದೆ"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ಫೈಲ್ ವರ್ಗಾವಣೆಗೆ USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ಫೋಟೋ ವರ್ಗಾವಣೆಗೆ USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ಗೆ USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ಪರಿಕರಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"ಅನ್ಲಾಗ್ ಆಡಿಯೋ ಪರಿಕರ ಪತ್ತೆಯಾಗಿದೆ"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"ಲಗತ್ತಿಸಲಾದ ಸಾಧನವು ಈ ಫೋನಿನೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ಡೀಬಗಿಂಗ್ ಸಂಪರ್ಕ"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"ಕೆಲಸ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2 ನೇ ಕೆಲಸದ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3 ನೇ ಕೆಲಸದ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ಈ ಪರದೆಯನ್ನು ಅನ್ಪಿನ್ ಮಾಡಲು, ಹಿಂದಕ್ಕೆ ಮತ್ತು ಸಮಗ್ರ ನೋಟ ಬಟನ್ಗಳನ್ನು ಸ್ಪರ್ಶಿಸಿ ಒತ್ತಿಹಿಡಿಯಿರಿ"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"ಸ್ಕ್ರೀನ್ ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"ಸ್ಕ್ರೀನ್ ಅನ್ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ಅನ್ಪಿನ್ ಮಾಡಲು ಪಿನ್ ಕೇಳು"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"ಅನ್ಪಿನ್ ಮಾಡಲು ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಕೇಳಿ"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"ಅನ್ಪಿನ್ ಮಾಡಲು ಪಾಸ್ವರ್ಡ್ ಕೇಳು"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"ಎಲ್ಲಾ ಭಾಷೆಗಳು"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"ಎಲ್ಲಾ ಪ್ರದೇಶಗಳು"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"ಹುಡುಕಿ"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಆನ್ ಮಾಡುವುದೇ?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"ನಿಮ್ಮ ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್ಗಳು, ಅಧಿಸೂಚನೆಗಳು, ಡೇಟಾ ಮತ್ತು ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ನ ಇತರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ಆನ್ ಮಾಡಿ"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು Android ನ ಹಳೆಯ ಆವೃತ್ತಿಗೆ ರಚಿಸಲಾಗಿದೆ ಮತ್ತು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡದಿರಬಹುದು. ಅಪ್ಡೇಟ್ಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಡೆವಲಪರ್ ಅನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"ಅಪ್ಡೇಟ್ಗಾಗಿ ಪರಿಶೀಲಿಸಿ"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"ನೀವು ಹೊಸ ಸಂದೇಶಗಳನ್ನು ಹೊಂದಿರುವಿರಿ"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"ವೀಕ್ಷಿಸಲು SMS ಅಪ್ಲಿಕೇಶನ್ ತೆರೆಯಿರಿ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"ಕೆಲವು ಕಾರ್ಯನಿರ್ವಹಣೆಗಳು ಸೀಮಿತವಾಗಿರಬಹುದು"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"ಧ್ವನಿಗಾಗಿ ಫೋನ್ ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ಪಾಪ್ಅಪ್ ವಿಂಡೋ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ಈ ಶಾರ್ಟ್ಕಟ್ಗೆ ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್ ಅಗತ್ಯವಿದೆ"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಕಪ್ ಮತ್ತು ಪುನಃಸ್ಥಾಪನೆಯನ್ನು ಬೆಂಬಲಿಸದಿರುವುದರಿಂದ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃಸ್ಥಾಪನೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ಅಪ್ಲಿಕೇಶನ್ ಸಹಿ ಹೊಂದಿಕೆಯಾಗದ ಕಾರಣದಿಂದ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃ ಸ್ಥಾಪನೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ಶಾರ್ಟ್ಕಟ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ಹೇಗಿದ್ದರೂ ತೆರೆಯಿರಿ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ಅಪಾಯಕಾರಿ ಅಪ್ಲಿಕೇಶನ್ ಕಂಡುಬಂದಿದೆ"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_2">%2$s</xliff:g> ಸ್ಲೈಸ್ಗಳನ್ನು <xliff:g id="APP_0">%1$s</xliff:g> ತೋರಿಸಲು ಬಯಸಿದೆ"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"ಎಡಿಟ್"</string>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 088d44f..cc568d38 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"ລະບົບ Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"ສະຫຼັບໄປໂປຣໄຟລ໌ສ່ວນຕົວ"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"ສະຫຼັບໄປໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ລາຍຊື່"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ເຂົ້າຫາລາຍຊື່ຂອງທ່ານ"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"ອະນຸຍາດ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ໃຫ້ເຂົ້າເຖິງລາຍຊື່ຜູ້ຕິດຕໍ່ຂອງທ່ານ"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ອະນຸຍາດໃຫ້ແອັບຯ ເຂົ້າເຖິງຄວາມສາມາດການໂທລະສັບຂອງອຸປະກອນ. ການກຳນົດສິດນີ້ເຮັດໃຫ້ແອັບຯສາມາດກວດສອບເບີໂທລະສັບ ແລະ ID ຂອງອຸປະກອນ, ບໍ່ວ່າການໂທຈະຍັງດຳເນີນຢູ່ ແລະເບີປາຍທາງເຊື່ອມຕໍ່ຢູ່ຫຼືບໍ່ກໍຕາມ."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"ສືບຕໍ່ການໂທຈາກແອັບອື່ນ"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ອະນຸຍາດໃຫ້ແອັບສືບຕໍ່ການໂທເຊິ່ງອາດຖືກເລີ່ມຕົ້ນໃນແອັບອື່ນ."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ອ່ານເບີໂທລະສັບ"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ອະນຸຍາດໃຫ້ແອັບເຂົ້າເຖິງເບີໂທລະສັບຂອງອຸປະກອນໄດ້."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ຂັດຂວາງບໍ່ໃຫ້ປິດໜ້າຈໍແທັບເລັດ"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ບໍ່ສາມາດເກັບຮັກສາລາຍນີ້ວມືໄວ້ໄດ້. ກະລຸນາເອົາລາຍນີ້ວມືທີ່ມີຢູ່ອອກໄປ."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ເວລາລາຍນີ້ວມືບໍ່ເຂົ້າເຖິງໄດ້. ລອງໃໝ່ອີກ."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"ຍົກເລີກການດຳເນີນການລາຍນີ້ວມືແລ້ວ."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ລອງໃໝ່ພາຍຫຼັງ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ລະບົບປິດການເຮັດວຽກຂອງເຊັນເຊີລາຍນິ້ວມືແລ້ວ."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ລອງໃໝ່ອີກຄັ້ງ."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"ນີ້ວມື <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"ເປີດ"</string>
<string name="sms" msgid="4560537514610063430">"ຂໍ້ຄວາມ"</string>
<string name="add_contact" msgid="7867066569670597203">"ເພີ່ມ"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"ເບິ່ງ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ຕັ້ງເວລາ"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ແທຣັກ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ບໍ່ມີບ່ອນເກັບຂໍ້ມູນພຽງພໍສຳລັບລະບົບ. ກວດສອບໃຫ້ແນ່ໃຈວ່າທ່ານມີພື້ນທີ່ຫວ່າງຢ່າງໜ້ອຍ 250MB ແລ້ວລອງໃໝ່."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">ເປີດເຄືອຂ່າຍ Wi-Fi ທີ່ມີໃຫ້</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi ແບບເປີດ"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi ຜູ້ໃຫ້ບໍລິການ"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"ກຳລັງເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi ແລ້ວ"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ບໍ່ສາມາດເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍ Wi‑Fi ໄດ້"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"ບໍ່ຕ້ອງການການອະນຸຍາດ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ລາຍການນີ້ອາດມີການເກັບເງິນ"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ຕົກລົງ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"ກຳລັງສາກໄຟ USB ອຸປະກອນນີ້"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB ກຳລັງສະໜອງໄຟໃຫ້ກັບອຸປະກອນທີ່ເຊື່ອມຕໍ່ກັນ"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB ສຳລັບການໂອນໄຟລ໌"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB ສຳລັບການໂອນໄຟລ໌"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ສຳລັບ MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ເຊື່ອມຕໍ່ກັບອຸປະກອນເສີມ USB ແລ້ວ"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"ແຕະເພື່ອເບິ່ງຕົວເລືອກເພີ່ມເຕີມ."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"ກວດພົບອຸປະກອນເສີມສຽງ"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"ອຸປະກອນທີ່ເຊື່ອມຕໍ່ນັ້ນບໍ່ສາມາດໃຊ້ຮ່ວມກັບໂທລະສັບນີ້ໄດ້. ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"ເຊື່ອມຕໍ່ການດີບັກຜ່ານ USB ແລ້ວ"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"ບ່ອນເຮັດວຽກ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"ບ່ອນເຮັດວຽກທີ 2 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"ບ່ອນເຮັດວຽກທີ 3 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ເພື່ອຍົກເລີກການປັກໝຸດໜ້າຈໍນີ້, ໃຫ້ແຕະປຸ່ມກັບຄືນ ແລະ ປຸ່ມພາບຮວມຄ້າງໄວ້"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"ປັກໝຸດໜ້າຈໍແລ້ວ"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"ຍົກເລີກການປັກໝຸນຫນ້າຈໍແລ້ວ"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ຖາມຫາ PIN ກ່ອນຍົກເລີກການປັກໝຸດ"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"ຖາມຫາຮູບແບບປົດລັອກກ່ອນຍົກເລີກການປັກໝຸດ"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"ຖາມຫາລະຫັດຜ່ານກ່ອນຍົກເລີກການປັກໝຸດ"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"ທຸກພາສາ"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"ທຸກຂົງເຂດ"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"ຄົ້ນຫາ"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"ເປີດໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກບໍ?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"ແອັບວຽກຂອງທ່ານ, ການແຈ້ງເຕືອນ, ຂໍ້ມູນ ແລະ ຄຸນສົມບັດໂປຣໄຟລ໌ວຽກຈະຖືກເປີດໃຊ້"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ເປີດ"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ແອັບນີ້ຖືກສ້າງຂຶ້ນສຳລັບ Android ເວີຊັນທີ່ເກົ່າກວ່າ ແລະ ອາດເຮັດວຽກໄດ້ບໍ່ປົກກະຕິ. ໃຫ້ລອງກວດສອບເບິ່ງອັບເດດ ຫຼື ຕິດຕໍ່ຜູ້ພັດທະນາ."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"ກວດເບິ່ງອັບເດດ"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"ທ່ານມີຂໍ້ຄວາມໃໝ່"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"ເປີດແອັບ SMS ເພື່ອເບິ່ງ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"ບາງຄວາມສາມາດນຳໃຊ້ອາດຈະຖືກຈຳກັດ"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ໂທລະສັບສຳລັບການໂທສຽງ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ໜ້າຈໍປັອບອັບ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ທາງລັດນີ້ຕ້ອງໃຊ້ແອັບເວີຊັນຫຼ້າສຸດ"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ບໍ່ສາມາດກູ້ທາງລັດຂຶ້ນມາໄດ້ເນື່ອງຈາກແອັບບໍ່ຮອງຮັບການສຳຮອງ ແລະ ກູ້ຂໍ້ມູນ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ບໍ່ສາມາດກູ້ທາງລັດຄືນມາໄດ້ເນື່ອງຈາກລາຍເຊັນແອັບບໍ່ກົງກັນ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ບໍ່ສາມາດກູ້ທາງລັດຄືນມາໄດ້"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ທາງລັດຖືກປິດການນຳໃຊ້"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ຖອນການຕິດຕັ້ງ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ຢືນຢັນການເປີດ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ກວດສອບແອັບທີ່ເປັນອັນຕະລາຍ"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ຕ້ອງການສະແດງ <xliff:g id="APP_2">%2$s</xliff:g> ສະໄລ້"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"ແກ້ໄຂ"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 746ac52..7c6f7f0 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Овозможува апликацијата да пристапи кон карактеристиките на телефонот на уредот. Оваа дозвола овозможува апликацијата да ги утврди телефонскиот број и ID на уредот, дали повикот е активен и далечинскиот број поврзан со повикот."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"пренасочи повици преку системот"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозволете ѝ на апликацијата да ги пренасочи повиците преку системот за да го подобри искуството при јавувањето."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"продолжување повик од друга апликација"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Овозможува апликацијата да продолжи повик започнат на друга апликација."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"да чита телефонски броеви"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Ѝ дозволува на апликацијата да пристапи до телефонските броеви на уредот."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"спречи режим на штедење кај таблет"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Премногу обиди. Обидете се повторно подоцна."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Премногу обиди. Сензорот за отпечатоци е оневозможен."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Обидете се повторно."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Отвори"</string>
<string name="sms" msgid="4560537514610063430">"Порака"</string>
<string name="add_contact" msgid="7867066569670597203">"Додај"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Прикажи"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Распоред"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Песна"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Капацитетот е речиси полн"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некои системски функции може да не работат"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 МБ и рестартирајте."</string>
@@ -1128,8 +1127,7 @@
<item quantity="other">Отворени Wi-Fi мрежи се достапни</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Поврзете се на отворена Wi‑Fi-мрежа"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Поврзете се на Wi‑Fi-мрежа на оператор"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Поврзување на отворена Wi‑Fi-мрежа"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Се поврзавте на Wi‑Fi-мрежа"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Не можеше да се поврзе на Wi‑Fi-мрежа"</string>
@@ -1214,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Не се потребни дозволи"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ова може да ве чини пари"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Во ред"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Уредов се полни преку USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Прикачениот уред се напојува преку USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB за пренос на датотеки"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB за пренос на фотографии"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за МИДИ"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Поврзан со USB додаток"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Допрете за повеќе опции."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Откриен е аналоген аудиододаток"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Приложениот уред не е компатибилен со телефонов. Допрете за да дознаете повеќе."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Поврзано е отстранување грешки преку USB"</string>
@@ -1661,9 +1669,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Работа <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Втора деловна <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Трета деловна <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"За откачување на екранов, допрете и задржете ги копчињата „Назад“ и „Краток преглед“."</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Екранот е закачен"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Екранот е откачен"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Прашај за PIN пред откачување"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Побарај шема за откл. пред откачување"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Прашај за лозинка пред откачување"</string>
@@ -1760,10 +1765,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Да се вклучи работниот профил?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Вашите работни апликации, известувања, податоци и други функции на работниот профил ќе бидат вклучени"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Вклучи"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Апликацијава е создадена за постара верзија на Android и може да не функционира правилно. Проверете за ажурирања или контактирајте со програмерот."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Проверка за ажурирање"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Имате нови пораки"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Отворете ја апликацијата за SMS за приказ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Некои функции се ограничени"</string>
@@ -1830,17 +1833,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Не е дозволен телефон за глас"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Појавен прозорец"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"За кратенкава е потребна најновата апликација"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не можеше да се врати кратенката бидејќи апликацијата не поддржува бекап и враќање"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не можеше да се врати кратенката бидејќи потписот на апликацијата не се совпаѓа"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не можеше да се врати кратенката"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Кратенката е оневозможена"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ДЕИНСТАЛИРАЈ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"СЕПАК ОТВОРИ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Откриена е штетна апликација"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> сака да прикажува делови од <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Измени"</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 022e077..cfde3c6 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"സുരക്ഷിത മോഡ്"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android സിസ്റ്റം"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"വ്യക്തിഗത പ്രൊഫൈലിലേക്ക് മാറുക"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് മാറുക"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"കോൺടാക്റ്റുകൾ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ ആക്സസ്സ് ചെയ്യുക"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"നിങ്ങളുടെ കോണ്ടാക്റ്റുകള് ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കുക"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ഉപകരണത്തിന്റെ ഫോൺ സവിശേഷതകൾ ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഈ അനുമതി ഫോൺ നമ്പർ, ഉപകരണ ഐഡികൾ, ഒരു കോൾ സജീവമാണോയെന്നത്, ഒരു കോൾ കണക്റ്റുചെയ്ത വിദൂര നമ്പർ എന്നിവ നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"കോളുകൾ സിസ്റ്റത്തിലൂടെ വിടുക"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"കോളിംഗ് അനുഭവം മെച്ചപ്പെടുത്തുന്നതിനായി തങ്ങളുടെ കോളുകൾ സിസ്റ്റത്തിലേയ്ക്ക് വഴിതിരിച്ചുവിടാൻ ആപ്പുകളെ അനുവദിക്കുന്നു."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"മറ്റൊരു ആപ്പിൽ നിന്നുള്ള കോൾ തുടരുക"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"മറ്റൊരു ആപ്പിൽ ആരംഭിച്ച കോൾ തുടരാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ഫോൺ നമ്പറുകൾ റീഡുചെയ്യൽ"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ഉപകരണത്തിന്റെ ഫോൺ നമ്പറുകൾ ആക്സസ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"സുഷുപ്തിയിലാകുന്നതിൽ നിന്ന് ടാബ്ലെറ്റിനെ തടയുക"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"വിരലടയാളം സംഭരിക്കാനാവില്ല. നിലവിലുള്ള വിരലടയാളം നീക്കംചെയ്യുക."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്റ് പ്രവർത്തനം."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"നിരവധി തവണ ശ്രമിച്ചതിനാൽ, വിരലടയാള സെൻസർ പ്രവർത്തനരഹിതമായി."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"വീണ്ടും ശ്രമിക്കൂ."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"കൈവിരൽ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"തുറക്കുക"</string>
<string name="sms" msgid="4560537514610063430">"സന്ദേശം"</string>
<string name="add_contact" msgid="7867066569670597203">"ചേർക്കുക"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"കാണുക"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ഷെഡ്യൂള്"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ട്രാക്ക്"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"സംഭരണയിടം കഴിഞ്ഞു"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"സിസ്റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">ലഭ്യമായ വൈഫൈ നെറ്റ്വർക്ക് തുറക്കുക</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"ലഭ്യമായ വൈഫൈ നെറ്റ്വർക്കിലേക്ക് കണക്റ്റുചെയ്യുക"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"കാരിയർ വൈഫൈ നെറ്റ്വര്ക്കിലേക്ക് കണക്റ്റ് ചെയ്യുക"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"ലഭ്യമായ വൈഫൈ നെറ്റ്വർക്കിലേക്ക് കണക്റ്റുചെയ്യുന്നു"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് കണക്റ്റുചെയ്തു"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"വൈ-ഫൈ നെറ്റ്വർക്കിലേക്ക് കണക്റ്റുചെയ്യാനായില്ല"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"അനുമതികളൊന്നും ആവശ്യമില്ല"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ഇത് നിങ്ങൾക്ക് പണച്ചെലവിനിടയാക്കാം"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ശരി"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB ഈ ഉപകരണം ചാർജജ് ചെയ്യുന്നു"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ഘടിപ്പിച്ചിട്ടുള്ള ഉപകരണത്തിന് USB വൈദ്യുതി നൽകുന്നു"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ഫയൽ കൈമാറ്റത്തിനുള്ള USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ഫോട്ടോ കൈമാറ്റത്തിനായുള്ള USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-യ്ക്കായുള്ള USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ഒരു USB ആക്സസ്സറി കണക്റ്റുചെയ്തു"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"അനലോഗ് ഓഡിയോ ആക്സസറി കണ്ടെത്തി"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"അറ്റാച്ചുചെയ്ത ഉപകരണം ഈ ഫോണിന് അനുയോജ്യമല്ല. കൂടുതലറിയാൻ ടാപ്പുചെയ്യുക."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തു"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"ഔദ്യോഗികം <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"രണ്ടാമത്തെ ഔദ്യോഗിക <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"മൂന്നാമത്തെ ഔദ്യോഗിക <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ഈ സ്ക്രീൻ അൺപിൻ ചെയ്യാൻ, ബാക്ക്, ചുരുക്കവിവരണം എന്നീ ബട്ടണുകൾ സ്പർശിച്ച് പിടിക്കുക"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"സ്ക്രീൻ പിൻ ചെയ്തു"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"സ്ക്രീൻ അൺപിൻ ചെയ്തു"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ചെയ്യുംമുമ്പ് പിൻ ചോദിക്കൂ"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"അൺപിന്നിനുമുമ്പ് അൺലോക്ക് പാറ്റേൺ ആവശ്യപ്പെടൂ"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"അൺപിന്നിനുമുമ്പ് പാസ്വേഡ് ആവശ്യപ്പെടൂ"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"എല്ലാ ഭാഷകളും"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"എല്ലാ പ്രദേശങ്ങളും"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"തിരയുക"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"ഔദ്യോഗിക പ്രൊഫൈൽ ഓണാക്കണോ?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകൾ, അറിയിപ്പുകൾ, ഡാറ്റ, മറ്റ് ഔദ്യോഗിക പ്രൊഫൈൽ ഫീച്ചറുകൾ എന്നിവ ഓണാക്കും"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ഓണാക്കുക"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ഈ ആപ്പ് Android-ന്റെ പഴയ പതിപ്പിനായി നിർമ്മിച്ചിരിക്കുന്നതിനാൽ ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. അപ്ഡേറ്റിനായി പരിശോധിക്കുക, അല്ലെങ്കിൽ ഡെവലപ്പറുമായി ബന്ധപ്പെടുക."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"അപ്ഡേറ്റിനായി പരിശോധിക്കുക"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"നിങ്ങൾക്ക് പുതിയ സന്ദേശങ്ങൾ ഉണ്ട്"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"കാണുന്നതിന് SMS ആപ്പ് തുറക്കുക"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"ചില പ്രവർത്തനക്ഷമതകൾ പരിമിതപ്പെടാം"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"ഫോൺ വോയ്സിന് അനുവദനീയമല്ല"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"പോപ്പ് അപ്പ് വിൻഡോ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ഈ കുറുക്കുവഴിക്ക് ഏറ്റവും പുതിയ ആപ്പ് ആവശ്യമാണ്"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ആപ്പ് \'ബാക്കപ്പും പുനഃസ്ഥാപിക്കലും\' പിന്തുണയ്ക്കാത്തതിനാൽ കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ആപ്പ് സിഗ്നേച്ചർ പൊരുത്തപ്പെടാത്തതിനാൽ കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"കുറുക്കുവഴി പ്രവർത്തനരഹിതമാണ്"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"അൺഇൻസ്റ്റാള് ചെയ്യുക"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"എന്തായാലും തുറക്കുക"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ദോഷകരമായ ആപ്പ് കണ്ടെത്തി"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_2">%2$s</xliff:g> സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP_0">%1$s</xliff:g> താൽപ്പര്യപ്പെടുന്നു"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"എഡിറ്റ് ചെയ്യുക"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 0da8708..d1a5455 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android सिस्टम"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"वैयक्तिक प्रोफाइलवर स्विच करा"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"कार्य प्रोफाइलवर स्विच करा"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"संपर्क"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"आपल्या संपर्कांवर प्रवेश"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला तुमचे संपर्क अॅक्सेस करू द्या"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अॅक्सेस करण्यास अॅपला अनुमती देते. ही परवानगी कॉल अॅक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रीमोट नंबर निर्धारित करण्यासाठी अॅपला अनुमती देते."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कॉल करण्याचा अनुभव सुधारण्यासाठी अॅपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"दुसऱ्या अॅपवरून कॉल करणे सुरू ठेवा"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"दुसऱ्या अॅपमध्ये सुरू झालेल्या कॉलला पुढे सुरू ठेवण्याची अॅपला अनुमती देते."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फोन नंबर वाचा"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"अॅपला डिव्हाइसच्या फोन नंबरमध्ये प्रवेश करण्याची अनुमती देते."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"टॅबलेट निष्क्रिय होण्यापासून प्रतिबंधित करा"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फिंगरप्रिंट स्टोअर केले जाऊ शकत नाही. कृपया विद्यमान फिंगरप्रिंट काढा."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"फिंगरप्रिंट टाइमआउट झाले. पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेंसर बंद आहे."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"पुन्हा प्रयत्न करा."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"उघडा"</string>
<string name="sms" msgid="4560537514610063430">"संदेश"</string>
<string name="add_contact" msgid="7867066569670597203">"जोडा"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"पहा"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"शेड्यूल"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ट्रॅक"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"संचयन स्थान संपत आहे"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">खुले वाय-फाय नेटवर्क उपलब्ध</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"खुल्या वाय-फाय नेटवर्कशी कनेक्ट करा"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"वाहक वाय-फाय नेटवर्कशी कनेक्ट करा"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"खुल्या वाय-फाय नेटवर्कशी कनेक्ट करत आहे"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"वाय-फाय नेटवर्कशी कनेक्ट केले"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"वाय-फाय नेटवर्कशी कनेक्ट करू शकत नाही"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"परवानग्या आवश्यक नाहीत"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"यासाठी आपले पैसे खर्च होऊ शकतात"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ठीक"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB हे डिव्हाइस चार्ज करत आहे"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB संलग्न केलेल्या डिव्हाइसला पॉवरचा पुरवठा करत आहे"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"स्थानांतरणासाठी USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"फोटो स्थानांतरणासाठी USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI साठी USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB उपसाधनावर कनेक्ट केले"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"अधिक पर्यायांसाठी टॅप करा."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"अॅनालॉग ऑडिओ अॅक्सेसरी आढळली"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"या फोनसह संलग्न केलेले डीव्हाइस सुसंगत नाही. अधिक जाणून घेण्यासाठी टॅप करा."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग करणे कनेक्ट केले"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2 रे कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3 रे कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"हा स्क्रीन अनपिन करण्यासाठी, मागे आणि अवलोकन बटणांना स्पर्श करून धरून ठेवा"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"स्क्रीन पिन केली"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"स्क्रीन अनपिन केली"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"अनपिन करण्यापूर्वी पिन साठी विचारा"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"अनपिन करण्यापूर्वी अनलॉक नमुन्यासाठी विचारा"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"अनपिन करण्यापूर्वी संकेतशब्दासाठी विचारा"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"कार्य प्रोफाइल चालू ठेवायची?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"तुमची कार्य अॅप्स, सूचना, डेटा आणि अन्य कार्य प्रोफाइल वैशिष्ट्ये चालू केली जातील"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"चालू करा"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"हे अॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"अपडेट आहे का ते तपासा"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"आपल्याकडे नवीन संदेश आहेत"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"पाहण्यासाठी SMS अॅप उघडा"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"काही कार्यक्षमता मर्यादित असू शकतात"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"व्हॉइसची फोनला अनुमती नाही"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"या शॉर्टकटला नवीनतम अॅपची आवश्यकता आहे"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद केलेला आहे"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"अनइंस्टॉल करा"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"तरीही उघडा"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अॅप आढळला"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवायचे आहेत"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"संपादित करा"</string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index afccd79..4c757df 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"အပလီကေးရှင်းအား ဖုန်းရဲ့ စွမ်းဆောင်ချက်များအား သုံးခွင့်ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် ဖုန်းနံပါတ်၊ စက်နံပါတ်၊ ဖုန်းခေါ်နေမှု ရှိမရှိနှင့် တဖက်မှ ဖုန်းနံပါတ် များအား သိရှိနိုင်ပါသည်"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ခေါ်ဆိုမှုများကို စနစ်မှတစ်ဆင့် ဖြတ်သန်းခွင့်ပြုပါ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ခေါ်ဆိုမှု အတွေ့အကြုံ ပိုမိုကောင်းမွန်လာစေရန်အတွက် အက်ပ်၏ ခေါ်ဆိုမှုအား စနစ်မှတစ်ဆင့် ဖြတ်သန်းရန် ခွင့်ပြုပါသည်။"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"အခြားအက်ပ်မှ ဖုန်းခေါ်ဆိုမှုကို ဆက်လက်ပြုလုပ်ပါ"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"အခြားအက်ပ်တွင် စတင်ထားသည့် ဖုန်းခေါ်ဆိုမှုကို ဆက်လက်ပြုလုပ်ရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ဖုန်းနံပါတ်များကို ဖတ်ရန်"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"အက်ပ်ကို စက်ပစ္စည်း၏ ဖုန်းနံပါတ်များအား အသုံးပြုခွင့်ပေးပါ။"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"တက်ပလက်အား ပိတ်ခြင်းမှ ကာကွယ်ခြင်း"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ကြိုးစာမှု အကြိမ်များနေ၏။ နောက်မှ ထပ်မံကြိုးစားပါ။"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"အကြိမ်အရေအတွက် အလွန်များနေပါပြီ။ လက်ဗွေဖတ်စနစ်ကို ပိတ်ထားပါသည်။"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ပြန်ကြိုးစားပါ"</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"ဖွင့်ရန်"</string>
<string name="sms" msgid="4560537514610063430">"စာပို့ရန်"</string>
<string name="add_contact" msgid="7867066569670597203">"ထည့်ရန်"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"ကြည့်ရန်"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"အချိန်ဇယား"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ရှာရန်"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1128,8 +1127,7 @@
<item quantity="one">Wi-Fi ကွန်ယက်ရရှိနိုင်သည်အား ဖွင့်ပါ</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"အများသုံး Wi‑Fi ကွန်ရက်သို့ ချိတ်ဆက်ပါ"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"ဝန်ဆောင်မှုပေးသူ Wi‑Fi ကွန်ရက်သို့ ချိတ်ဆက်ပါ"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"အများသုံး Wi‑Fi ကွန်ရက်သို့ ချိတ်ဆက်နေသည်"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Wi‑Fi ကွန်ရက်သို့ ချိတ်ဆက်ပြီးပါပြီ"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi‑Fi ကွန်ရက်သို့ ချိတ်ဆက်၍ မရပါ"</string>
@@ -1214,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"ခွင့်ပြုချက်မလိုအပ်ပါ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"သင့်အတွက် ပိုက်ဆံကုန်ကျနိုင်ပါသည်"</string>
<string name="dlg_ok" msgid="7376953167039865701">"အိုကေ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"ဤစက်ကို USB ဖြင့် အားသွင်းနေသည်"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ချိတ်ဆက်ထားသည့် စက်ပစ္စည်းကို USB မှတစ်ဆင့် အားသွင်းနေသည်"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ဖိုင်လွှဲပြောင်းရန်အတွက် USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ဓာတ်ပုံလွှဲပြောင်းရန်အတွက် USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI အတွက် USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBတွဲဖက်ပစ္စည်းအား ချိတ်ဆက်ထားသည်"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"နောက်ထပ်ရွေးချယ်စရာများအတွက် တို့ပါ။"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"အန်နာလော့ အသံကိရိယာကို တွေ့ထားပါသည်"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"တပ်ဆင်ထားသော ကိရိယာကို ဤဖုန်းနှင့် တွဲသုံး၍မရပါ။ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားရှာပြင်စနစ် ချိတ်ဆက်ထားသည်"</string>
@@ -1659,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"အလုပ် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"ဒုတိယအလုပ် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"တတိယအလုပ် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ဤမျက်နှာပြင်ကို ပင်ဖြုတ်ရန်အတွက် \'နောက်သို့\' နှင့် \'အနှစ်ချုပ်\' ခလုတ်များကို အတူတို့၍ ထိထားပါ"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"မျက်နှာပြင်ကို ပင်ထိုးထား"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"မျက်နှာပြင် ပင်ထိုးမှု ဖြတ်လိုက်ပြီ"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ပင်မဖြုတ်မီမှာ PIN ကို မေးကြည့်ရန်"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"ပင်မဖြုတ်မီမှာ သော့ဖွင့် ရေးဆွဲမှုပုံစံကို မေးကြည့်ရန်"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"ပင်မဖြုတ်မီမှာ စကားဝှက်ကို မေးကြည့်ရန်"</string>
@@ -1758,10 +1763,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"အလုပ်ပရိုဖိုင် ဖွင့်လိုသလား။"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"သင်၏ အလုပ်အက်ပ်၊ အကြောင်းကြားချက်၊ ဒေတာနှင့် အခြားအလုပ်ပရိုဖိုင် ဝန်ဆောင်မှုများကို ဖွင့်လိုက်ပါမည်"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ဖွင့်ပါ"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ဤအက်ပ်ကို Android ဗားရှင်းဟောင်းအတွက် ပြုလုပ်ထားခြင်းဖြစ်ပြီး ပုံမှန်အလုပ်မလုပ်နိုင်ပါ။ အပ်ဒိတ်များအတွက် ရှာကြည့်ပါ သို့မဟုတ် ဆော့ဖ်ဝဲအင်ဂျင်နီယာကို ဆက်သွယ်ပါ။"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"အပ်ဒိတ်အတွက် စစ်ကြည့်ရန်"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"သင့်ထံတွင် စာအသစ်များရောက်နေသည်"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"ကြည့်ရှုရန် SMS အက်ပ်ကိုဖွင့်ပါ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"အချို့လုပ်ဆောင်ချက်များ ကန့်သတ်ချက်ရှိနိုင်သည်"</string>
@@ -1828,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"စကားသံအတွက် ဖုန်းကို ခွင့်မပြုပါ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ပေါ့ပ်အပ် ဝင်းဒိုး"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ဤဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုနိုင်ရန် နောက်ဆုံးထွက်အက်ပ် လိုအပ်ပါသည်"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"အက်ပ်သည် မိတ္တူကူးခြင်းနှင့် ပြန်ယူခြင်းကို ပံ့ပိုးခြင်းမရှိသည့်အတွက် ဖြတ်လမ်းလင့်ခ်ကို ပြန်ယူ၍မရပါ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"အက်ပ်လက်မှတ် မတူညီသည့်အတွက် ဖြတ်လမ်းလင့်ခ်များကို ပြန်ယူ၍မရပါ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ဖြတ်လမ်းလင့်ခ်ကို ပြန်ယူ၍မရပါ"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ဖြတ်လမ်းလင့်ခ် ပိတ်ထားသည်"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ဖြုတ်ရန်"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ဘာဖြစ်ဖြစ် ဖွင့်ရန်"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"အန္တရာယ်ရှိသော အက်ပ်ကို တွေ့ရှိထားသည်"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> သည် <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များကို ပြသလိုသည်"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"တည်းဖြတ်ရန်"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 4cbdd17..1b73237 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Bytt til personlig profil"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Bytt til jobbprofil"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"se kontaktene dine"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Gi <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilgang til kontaktene dine"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Lar appen bruke enhetens telefonfunksjoner. Med denne tillatelsen kan appen finne telefonnummer og enhets-ID-er, registrere om en samtale pågår, og se det eksterne nummeret det opprettes en forbindelse med via oppringing."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"send anrop gjennom systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Lar appen sende anrop gjennom systemet for å forbedre anropsopplevelsen."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"fortsette et anrop fra en annen app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Lar appen fortsette et anrop som ble startet i en annen app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"les telefonnumre"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Gir appen tilgang til telefonnumrene til enheten."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"hindre nettbrettet fra å gå over til sovemodus"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrykket kan ikke lagres. Fjern et eksisterende fingeravtrykk."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tidsavbrudd for fingeravtrykk er nådd. Prøv på nytt."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Fingeravtrykk-operasjonen ble avbrutt."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Fingeravtrykk-operasjonen ble avbrutt av brukeren."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"For mange forsøk. Prøve på nytt senere."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"For mange forsøk. Fingeravtrykkssensoren er slått av."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Prøv igjen."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Åpne"</string>
<string name="sms" msgid="4560537514610063430">"Melding"</string>
<string name="add_contact" msgid="7867066569670597203">"Legg til"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Se"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Oversikt"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Spor"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Åpent Wi-Fi-nettverk er tilgjengelig</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Koble til et åpent Wi‑Fi-nettverk"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Koble til operatørens Wi-Fi-nettverk"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Kobler til åpent Wi-Fi-nettverk"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Koblet til Wi-Fi-nettverk"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Kunne ikke koble til Wi-Fi-nettverket"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Trenger ingen rettigheter"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste deg penger"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Enheten lades via USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Den tilkoblede enheten får strøm via USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for filoverføring"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for bildeoverføring"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Koblet til et USB-tilbehør"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Trykk for å få flere alternativ."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analogt lydtilbehør ble oppdaget"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Den tilkoblede enheten er ikke kompatibel med denne telefonen. Trykk for å finne ut mer."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-feilsøking tilkoblet"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Jobb-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Andre <xliff:g id="LABEL">%1$s</xliff:g> for jobben"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Tredje <xliff:g id="LABEL">%1$s</xliff:g> for jobben"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"For å løsne denne skjermen, trykk på og hold inne Tilbake- og Oversikt-knappene"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Skjermen er festet"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Skjermen er løsnet"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"PIN-kode for å løsne apper"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Krev opplåsingsmønster for å løsne apper"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Krev passord for å løsne apper"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Alle språk"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Alle områder"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Søk"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Vil du slå på jobbprofilen?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Jobbappene dine samt varsler, data og andre funksjoner i jobbprofilen din blir slått på"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Slå på"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Denne appen er utviklet for en eldre versjon av Android og fungerer kanskje ikke som den skal. Prøv å se etter oppdateringer, eller kontakt utvikleren."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Se etter oppdatering"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Du har nye meldinger"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Åpne SMS-appen for å se"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Enkelte funksjoner kan være begrenset"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefonen har ikke tillatelse til tale"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Forgrunnsvindu"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Denne snarveien krever den nyeste appen"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Kunne ikke gjenopprette snarveien fordi appen ikke støtter sikkerhetskopiering og gjenoppretting"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Kunne ikke gjenopprette snarveien på grunn av manglende samsvar for appsignaturen"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kunne ikke gjenopprette snarveien"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Snarveien er slått av"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"AVINSTALLER"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ÅPNE LIKEVEL"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"En skadelig app ble oppdaget"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> vil vise <xliff:g id="APP_2">%2$s</xliff:g>-utsnitt"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Endre"</string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 014d4f2..4a2aad6 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -235,7 +235,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"हवाइजहाज मोड"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"हवाइजहाज मोड खुला छ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"हवाइजहाज मोड बन्द छ"</string>
- <string name="global_action_settings" msgid="1756531602592545966">"सेटिङ्हरू"</string>
+ <string name="global_action_settings" msgid="1756531602592545966">"सेटिङहरू"</string>
<string name="global_action_assist" msgid="3892832961594295030">"सहायता दिनुहोस्"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज सहायता"</string>
<string name="global_action_lockdown" msgid="2277328351790053477">"लकडाउन प्रविष्ट गर्नुहोस्"</string>
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"एन्ड्रोइड प्रणाली"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"व्यक्तिगत प्रोफाइलमा बदल्नुहोस्"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"कार्य प्रोफाइलमा बदल्नुहोस्"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"सम्पर्कहरू"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"तपाईँको सम्पर्कमाथि पहुँच गर्नुहोस्"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई आफ्ना सम्पर्क ठेगानाहरूमाथि पहुँच राख्न दिनुहोस्"</string>
@@ -402,7 +400,7 @@
<string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको TV मा उपलब्ध हुनुपर्छ।"</string>
<string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको फोनमा उपलब्ध हुनुपर्छ।"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"तपाईँका अडियो सेटिङहरू परिवर्तन गर्नुहोस्"</string>
- <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"अनुप्रयोगलाई ग्लोबल अडियो सेटिङ्हरू परिमार्जन गर्न अनुमति दिन्छ, जस्तै आवाजको मात्रा र आउटपुटको लागि कुन स्पिकर प्रयोग गर्ने।"</string>
+ <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"अनुप्रयोगलाई ग्लोबल अडियो सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ, जस्तै आवाजको मात्रा र आउटपुटको लागि कुन स्पिकर प्रयोग गर्ने।"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"अडियो रेकर्ड गर्नुहोस्"</string>
<string name="permdesc_recordAudio" msgid="4245930455135321433">"यस अनुप्रयोगले जुनसुकै समय माइक्रोफोनको प्रयोग गरी अडियो रेकर्ड गर्न सक्छ।"</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"SIM मा आदेशहरू पठाउन दिनुहोस्"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"उपकरणको फोन विशेषताहरूको पहुँच गर्न अनुप्रयोगलाई अनुमति दिन्छ। यस अनुमतिले फोन नम्बर र उपकरणको IDs, कल सक्षम छ कि छैन र कलद्वारा जोडिएको टाढाको नम्बर निर्धारण गर्न अनुमति दिन्छ।"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"प्रणाली मार्फत कल गर्न दिनुहोस्"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"कल गर्दाको अनुभवलाई सुधार्न यस अनुप्रयोगलाई प्रणाली मार्फत कलहरू गर्न अनुमति दिन्छ।"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्नुहोस्"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"यस अनुप्रयोगलाई अर्को अनुप्रयोगमा सुरु गरिएको कल जारी राख्ने अनुमति दिन्छ।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"फोन नम्बरहरू पढ्ने"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"उक्त अनुप्रयोगलाई यस यन्त्रको फोन नम्बरहरूमाथि पहुँच राख्न दिनुहोस्।"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ट्याब्लेटलाई निन्द्रामा जानबाट रोक्नुहोस्"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"औँठाछाप भण्डारण गर्न सकिँदैन। कृपया अवस्थित औठाछाप हटाउनुहोस्।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"औँठाछाप सञ्चालन रद्द गरियो।"</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"धेरै प्रयासहरू। केहि समय पछि पुन: प्रयास गर्नुहोला"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"अत्यन्त धेरै प्रयासहरू। फिंगरप्रिन्ट सेन्सरलाई असक्षम पारियो।"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"पुन: प्रयास गर्नुहोला।"</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"खोल्नुहोस्"</string>
<string name="sms" msgid="4560537514610063430">"सन्देश"</string>
<string name="add_contact" msgid="7867066569670597203">"थप्नुहोस्"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"हेर्नुहोस्"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"समयतालिका"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ट्र्याक गर्नुहोस्"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1024,7 +1020,7 @@
<!-- no translation found for whichApplicationNamed (8260158865936942783) -->
<skip />
<string name="whichApplicationLabel" msgid="7425855495383818784">"पूर्ण कारबाही"</string>
- <string name="whichViewApplication" msgid="3272778576700572102">"साथमा खोल्नुहोस्"</string>
+ <string name="whichViewApplication" msgid="3272778576700572102">"निम्नमार्फत खोल्नुहोस्"</string>
<!-- String.format failed for translation -->
<!-- no translation found for whichViewApplicationNamed (2286418824011249620) -->
<skip />
@@ -1137,8 +1133,7 @@
<item quantity="one">खुल्ला Wi-Fi सञ्जाल उपलब्ध छ</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"खुला Wi‑Fi नेटवर्कमा जडान गर्नुहोस्"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"सेवा प्रदायकको Wi‑Fi नेटवर्कमा जडान गर्नुहोस्"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"खुला Wi‑Fi नेटवर्कमा जडान गर्दै"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Wi‑Fi नेटवर्कमा जडान गरियो"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi‑Fi नेटवर्कमा जडान गर्न सकिएन"</string>
@@ -1222,13 +1217,23 @@
<string name="no_permissions" msgid="7283357728219338112">"कुनै अनुमति आवश्यक छैन"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"सायद तपाईँलाई पैसा पर्न सक्छ।"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ठीक छ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"यस यन्त्रलाई USB मार्फत चार्ज गर्दै"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"संलग्न गरिएको यन्त्रमा USB मार्फत पावर आपूर्ति गरिँदै"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"फाइल स्थानान्तरणको लागि USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"तस्बिर स्थानान्तरणको लागि USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI को लागि USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB सहायकमा जोडिएको छ"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"थप विकल्पहरूका लागि ट्याप गर्नुहोस्।"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"एनालग अडियोको सहायक उपकरण पत्ता लाग्यो"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"संलग्न गरिएको यन्त्र यो फोनसँग कम्प्याटिबल छैन। थप जान्न ट्याप गर्नुहोस्।"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डिबग गर्ने सुविधा सुचारू छ"</string>
@@ -1459,7 +1464,7 @@
<string name="launchBrowserDefault" msgid="2057951947297614725">"ब्राउजर सुरु गर्ने हो?"</string>
<string name="SetupCallDefault" msgid="5834948469253758575">"कल स्वीकार गर्नुहुन्छ?"</string>
<string name="activity_resolver_use_always" msgid="8017770747801494933">"सधैँ"</string>
- <string name="activity_resolver_use_once" msgid="2404644797149173758">"एउटा मात्र"</string>
+ <string name="activity_resolver_use_once" msgid="2404644797149173758">"एक पटक मात्र"</string>
<string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s कार्य प्रोफाइल समर्थन गर्दैन"</string>
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"ट्याब्लेट"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
@@ -1667,9 +1672,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"कार्यालयको दोस्रो <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"कार्यालयको तेस्रो <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"यस स्क्रिनलाई अनपनि गर्न पछाडि जाने र परिदृश्य बटनहरूलाई छोएर थिची राख्नुहोस्"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"स्क्रिन पिन गरियो"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"स्क्रिन अनपिन गरियो"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"पिन निकाल्नुअघि PIN सोध्नुहोस्"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"पिन निकाल्नुअघि खोल्ने ढाँचा सोध्नुहोस्"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"पिन निकाल्नुअघि पासवर्ड सोध्नुहोस्"</string>
@@ -1763,15 +1765,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"सम्पूर्ण भाषाहरू"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"सबै क्षेत्रहरू"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"खोज"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"कार्य प्रोफाइल सक्रिय गर्ने?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"तपाईंका कार्यसम्बन्धी अनुप्रयोग, सूचना, डेटा र कार्य प्रोफाइलका अन्य सुविधाहरू सक्रिय गरिने छन्"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"सक्रिय गर्नुहोस्"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"यो अनुप्रयोग Android को पुरानो संस्करणका लागि बनाइएको हुनाले यसले सही ढङ्गले काम नगर्न सक्छ। अद्यावधिकहरू उपलब्ध छन् वा छैनन् भनी जाँच गरी हेर्नुहोस् वा यसको विकासकर्तालाई सम्पर्क गर्नुहोस्।"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"अद्यावधिक उपलब्ध छ वा छैन भनी जाँच गर्नुहोस्"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"तपाईंलाई नयाँ सन्देश आएको छ"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"हेर्नका लागि SMS अनुप्रयोग खोल्नुहोस्"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"केही कार्य सीमित हुनसक्छ"</string>
@@ -1791,7 +1789,7 @@
<string name="conference_call" msgid="3751093130790472426">"सम्मेलन कल"</string>
<string name="tooltip_popup_title" msgid="5253721848739260181">"उपकरणको वर्णन"</string>
<string name="app_category_game" msgid="5431836943981492993">"खेलहरू"</string>
- <string name="app_category_audio" msgid="1659853108734301647">"संगीत तथा अडियो"</string>
+ <string name="app_category_audio" msgid="1659853108734301647">"सङ्गीत तथा अडियो"</string>
<string name="app_category_video" msgid="2728726078629384196">"चलचित्र तथा भिडियो"</string>
<string name="app_category_image" msgid="4867854544519846048">"तस्बिर तथा छविहरू"</string>
<string name="app_category_social" msgid="5842783057834965912">"सामाजिक तथा सञ्चार"</string>
@@ -1838,17 +1836,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"फोनमार्फत भ्वाइस कल गर्न मिल्दैन"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"पपअप विन्डो"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"यो सर्टकटलाई पछिल्लो अनुप्रयोग आवश्यक हुन्छ"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अनुप्रयोगले ब्याकअप तथा पुनर्स्थापनालाई समर्थन नगर्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अनुप्रयोगमा प्रयोग गरिने हस्ताक्षर नमिल्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"सर्टकट असक्षम पारिएको छ"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"स्थापना रद्द गर्नु…"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"जे भए पनि खोल्नुहोस्"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"हानिकारक अनुप्रयोग भेटियो"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ले <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन चाहन्छ"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"सम्पादन गर्नुहोस्"</string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 5412d47..ae3014d 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"ਸੁਰੱਖਿਅਤ ਮੋਡ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"ਨਿੱਜੀ ਪ੍ਰੋਫਾਈਲ ਵਰਤੋ"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਰਤੋ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ਸੰਪਰਕ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਸੰਪਰਕਾਂ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦਿਓ"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੀਆਂ ਫ਼ੋਨ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਜਾਜ਼ਤ ਐਪ ਨੂੰ ਫ਼ੋਨ ਨੰਬਰ ਅਤੇ ਡੀਵਾਈਸ ਆਈ.ਡੀ. ਨਿਰਧਾਰਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ, ਇੱਕ ਕਾਲ ਕਿਰਿਆਸ਼ੀਲ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਰਿਮੋਟ ਨੰਬਰ ਇੱਕ ਕਾਲ ਨਾਲ ਕਨੈਕਟ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਰੂਟ ਕਰੋ"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ਕਾਲ ਕਰਨ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ ਐਪ ਨੂੰ ਇਸਦੀਆਂ ਕਾਲਾਂ ਨੂੰ ਸਿਸਟਮ ਰਾਹੀਂ ਰੂਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"ਕਿਸੇ ਹੋਰ ਐਪ ਤੋਂ ਕਾਲ ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ਐਪ ਨੂੰ ਉਹ ਕਾਲ ਜਾਰੀ ਰੱਖਣ ਦਿਓ ਜਿਸਨੂੰ ਹੋਰ ਐਪ ਤੋਂ ਚਾਲੂ ਕੀਤਾ ਗਿਆ ਸੀ।"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ਫ਼ੋਨ ਨੰਬਰ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੇ ਫ਼ੋਨ ਨੰਬਰਾਂ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ਟੈਬਲੈੱਟ ਨੂੰ ਸਲੀਪ ਤੇ ਜਾਣ ਤੋਂ ਰੋਕੋ"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਮੌਜੂਦਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਟਾਓ।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"ਫਿੰਗਰ"</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ. ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"ਖੋਲ੍ਹੋ"</string>
<string name="sms" msgid="4560537514610063430">"ਸੁਨੇਹਾ ਭੇਜੋ"</string>
<string name="add_contact" msgid="7867066569670597203">"ਸ਼ਾਮਲ ਕਰੋ"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"ਦੇਖੋ"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"ਸਮਾਂ-ਸੂਚੀ"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ਟਰੈਕ ਕਰੋ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">ਉਪਲਬਧ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਖੋਲ੍ਹੋ</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"ਖੁੱਲ੍ਹੇ ਵਾਈ‑ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੋਵੋ"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"ਕੈਰੀਅਰ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"ਖੁੱਲ੍ਹੇ ਵਾਈ‑ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"ਵਾਈ‑ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"ਵਾਈ‑ਫਾਈ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"ਕੋਈ ਅਨੁਮਤੀਆਂ ਲੁੜੀਂਦੀਆਂ ਨਹੀਂ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ਇਸ ਨਾਲ ਤੁਹਾਨੂੰ ਖਰਚਾ ਪੈ ਸਕਦਾ ਹੈ"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ਠੀਕ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"ਇਹ ਡੀਵਾਈਸ USB ਰਾਹੀਂ ਚਾਰਜ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ਨੱਥੀ ਕੀਤੇ ਡੀਵਾਈਸ ਨੂੰ USB ਰਾਹੀਂ ਪਾਵਰ ਮਿਲ ਰਹੀ ਹੈ"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ਫ਼ਾਈਲ ਟ੍ਰਾਂਸਫ਼ਰ ਲਈ USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ਫ਼ੋਟੋ ਟ੍ਰਾਂਸਫ਼ਰ ਲਈ USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ਲਈ USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ਇੱਕ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"ਹੋਰ ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"ਐਨਾਲੌਗ ਆਡੀਓ ਉਪਸਾਧਨ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"ਨੱਥੀ ਕੀਤਾ ਡੀਵਾਈਸ ਇਸ ਫ਼ੋਨ ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। ਹੋਰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ਡੀਬਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"ਕੰਮ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"ਦੂਸਰੀ ਕਾਰਜ-ਸਥਾਨ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"ਤੀਸਰੀ ਕਾਰਜ-ਸਥਾਨ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ਇਸ ਸਕ੍ਰੀਨ ਨੂੰ ਅਨਪਿੰਨ ਕਰਨ ਲਈ, \'ਪਿੱਛੇ\' ਅਤੇ \'ਰੂਪ-ਰੇਖਾ\' ਬਟਨਾਂ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"ਸਕ੍ਰੀਨ ਪਿੰਨ ਕੀਤੀ"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"ਸਕ੍ਰੀਨ ਅਨਪਿਨ ਕੀਤੀ"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ਅਨਪਿੰਨ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਪਿੰਨ ਮੰਗੋ"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"ਅਨਪਿੰਨ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਅਣਲਾਕ ਪੈਟਰਨ ਵਾਸਤੇ ਪੁੱਛੋ"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"ਅਨਪਿੰਨ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਪਾਸਵਰਡ ਮੰਗੋ"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"ਸਾਰੀਆਂ ਭਾਸ਼ਾਵਾਂ"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"ਸਾਰੇ ਖੇਤਰ"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"ਖੋਜੋ"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"ਕੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਚਾਲੂ ਕਰਨੀ ਹੈ?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"ਤੁਹਾਡੀਆਂ ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ, ਸੂਚਨਾਵਾਂ, ਡਾਟਾ ਅਤੇ ਹੋਰ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚਾਲੂ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ਚਾਲੂ ਕਰੋ"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ਇਹ ਐਪ Android ਦੇ ਕਿਸੇ ਵਧੇਰੇ ਪੁਰਾਣੇ ਵਰਜਨ ਲਈ ਬਣਾਈ ਗਈ ਸੀ ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। ਅੱਪਡੇਟਾਂ ਲਈ ਜਾਂਚ ਕਰੋ ਜਾਂ ਵਿਕਾਸਕਾਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"ਅੱਪਡੇਟ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"ਤੁਹਾਨੂੰ ਨਵੇਂ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਹੋਏ ਹਨ"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"ਦੇਖਣ ਲਈ SMS ਐਪ ਖੋਲ੍ਹੋ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"ਕੁਝ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਸੀਮਿਤ ਹੋ ਸਕਦੀ ਹੈ"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"ਫ਼ੋਨ ਰਾਹੀਂ ਅਵਾਜ਼ੀ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ਪੌਪਅੱਪ ਵਿੰਡੋ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਨਵੀਨਤਮ ਐਪ ਦੀ ਲੋੜ ਹੈ"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ ਕਿਉਂਕਿ ਐਪ \'ਬੈਕਅੱਪ ਅਤੇ ਮੁੜ-ਬਹਾਲੀ\' ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਦੀ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ਐਪ ਹਸਤਾਖਰ ਦਾ ਮੇਲ ਨਾ ਹੋਣ ਕਾਰਨ ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ਸ਼ਾਰਟਕੱਟ ਬੰਦ ਕੀਤਾ ਹੋਇਆ ਹੈ"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ਅਣਸਥਾਪਤ ਕਰੋ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ਫਿਰ ਵੀ ਖੋਲ੍ਹੋ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ਹਾਨੀਕਾਰਕ ਐਪ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ਦੀ <xliff:g id="APP_2">%2$s</xliff:g> ਦੇ ਹਿੱਸੇ ਦਿਖਾਉਣ ਦੀ ਇੱਛਾ ਹੈ"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"ਸੰਪਾਦਨ ਕਰੋ"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 11fc517..8fe4729 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada de outro app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que o app continue uma chamada que foi iniciada em outro app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de telefone"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permite que o app acesse os número de telefone do dispositivo."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"impedir modo de inatividade do tablet"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Excesso de tentativas. Tente novamente mais tarde."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Excesso de tentativas. Sensor de impressão digital desativado."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Tente novamente."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="sms" msgid="4560537514610063430">"Mensagem"</string>
<string name="add_contact" msgid="7867066569670597203">"Adicionar"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Ver"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Programar"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Rastrear"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1128,8 +1127,7 @@
<item quantity="other">Abrir redes Wi-Fi disponíveis</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Conectar-se a uma rede Wi‑Fi aberta"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Conectar à rede Wi‑Fi da operadora"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Conectando-se a uma rede Wi‑Fi aberta"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Conectado a uma rede Wi‑Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Não foi possível conectar-se à rede Wi‑Fi"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"isso pode lhe custar dinheiro"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB carregando este dispositivo"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB fornecendo energia ao dispositivo conectado"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferência de arquivos"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferência de fotos"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Toque para ver mais opções."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Acessório de áudio analógico detectado"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Trabalho: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Segundo <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Terceiro <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Para liberar esta tela, mantenha os botões Voltar e Visão geral pressionados"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Tela fixada"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Tela liberada"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pedir PIN antes de liberar"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Pedir padrão de desbloqueio antes de liberar"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Pedir senha antes de liberar"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Ativar o perfil de trabalho?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Seus apps, notificações, dados e outros recursos do perfil de trabalho serão ativados"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Ativar"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Este app foi criado para uma versão mais antiga do Android e pode não funcionar corretamente. Tente verificar se há atualizações ou entre em contato com o desenvolvedor."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Procurar atualizações"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Você tem mensagens novas"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Abra o app de SMS para ver"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Algumas funcionalidades são limitadas"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Smartphone não autorizado para voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Esse atalho requer o app mais recente"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque o app não é compatível com backup e restauração"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido à incompatibilidade de assinatura de apps"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DESINSTALAR"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ABRIR MESMO ASSIM"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"App nocivo detectado"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> quer mostrar partes do app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Editar"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index a624066..04bd394 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Mudar para o perfil pessoal"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Mudar para o perfil de trabalho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"aceder aos contactos"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Permitir que a aplicação <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda aos seus contactos"</string>
@@ -287,7 +285,7 @@
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"gravar áudio"</string>
<string name="permgrouprequest_microphone" msgid="8065941268709600606">"Permitir que a aplicação <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grave áudio"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Câmara"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"tirar fotografias e gravar vídeos"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"tirar fotos e gravar vídeos"</string>
<string name="permgrouprequest_camera" msgid="810824326507258410">"Permitir que a aplicação <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tire fotos e grave vídeos"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telemóvel"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerir chamadas"</string>
@@ -407,7 +405,7 @@
<string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta aplicação pode gravar áudio através do microfone a qualquer momento."</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que a aplicação envie comandos para o SIM. Esta ação é muito perigosa."</string>
- <string name="permlab_camera" msgid="3616391919559751192">"tirar fotografias e vídeos"</string>
+ <string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e vídeos"</string>
<string name="permdesc_camera" msgid="5392231870049240670">"Esta aplicação pode tirar fotos e gravar vídeos através da câmara a qualquer momento."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Permite à aplicação controlar o vibrador."</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que a aplicação aceda às funcionalidades de telefone do dispositivo. Esta autorização permite que a aplicação determine o número de telefone e IDs do dispositivo, se alguma chamada está ativa e qual o número remoto ligado por uma chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas através do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que a aplicação encaminhe as respetivas chamadas através do sistema de modo a melhorar a experiência da chamada."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada a partir de outra aplicação"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite à aplicação continuar uma chamada iniciada noutra aplicação."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler os números de telefone"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permite à aplicação aceder aos números de telefone do dispositivo."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"impedir que o tablet entre em inactividade"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não é possível armazenar a impressão digital. Remova uma impressão digital existente."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Foi atingido o limite de tempo da impressão digital. Tente novamente."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Operação de impressão digital cancelada."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Operação de impressão digital cancelada pelo utilizador."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Demasiadas tentativas. Tente novamente mais tarde."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Demasiadas tentativas. Sensor de impressões digitais desativado."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Tente novamente."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="sms" msgid="4560537514610063430">"Mensagem"</string>
<string name="add_contact" msgid="7867066569670597203">"Adicionar"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Ver"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Agendar"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Monitorizar"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Rede Wi-Fi aberta disponível</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Ligar à rede Wi-Fi aberta"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Estabelecer ligação à rede Wi‑Fi do operador"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"A ligar à rede Wi-Fi aberta…"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Ligado à rede Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Não foi possível ligar à rede Wi-Fi"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"isto poderá estar sujeito a custos"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Carregamento deste dispositivo por USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Fornecimento de energia ao dispositivo ligado por USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferência de ficheiros"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferência de fotos"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ligado a um acessório USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Toque para obter mais opções."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Acessório de áudio analógico detetado"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"O dispositivo ligado não é compatível com este telemóvel. Toque para saber mais."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2.º <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3.º <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Para soltar este ecrã, toque sem soltar nos botões Anterior e Vista geral"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Ecrã fixo"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Ecrã solto"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pedir PIN antes de soltar"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Pedir sequência de desbloqueio antes de soltar"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Pedir palavra-passe antes de soltar"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Todos os idiomas"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Todas as regiões"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Pesquisa"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Ativar o perfil de trabalho?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"As aplicações de trabalho, as notificações, os dados e outras funcionalidades do perfil de trabalho serão desativados"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Ativar"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Esta aplicação foi concebida para uma versão mais antiga do Android e pode não funcionar corretamente. Experimente verificar se existem atualizações ou contacte o programador."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Verificar se existem atualizações"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Tem mensagens novas"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Abra a aplicação de SMS para ver"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Algumas funcionalid. limitadas"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telemóvel não permitido para voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Este atalho requer a aplicação mais recente."</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque a aplicação não é compatível com a funcionalidade de cópia de segurança e restauro."</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido a uma falha de correspondência entre as assinaturas das aplicações."</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho."</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado."</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DESINSTALAR"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ABRIR MESMO ASSIM"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Aplicação prejudicial detetada"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"A aplicação <xliff:g id="APP_0">%1$s</xliff:g> pretende mostrar partes da aplicação <xliff:g id="APP_2">%2$s</xliff:g>."</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Editar"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 11fc517..8fe4729 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"continuar uma chamada de outro app"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite que o app continue uma chamada que foi iniciada em outro app."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ler números de telefone"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permite que o app acesse os número de telefone do dispositivo."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"impedir modo de inatividade do tablet"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Excesso de tentativas. Tente novamente mais tarde."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Excesso de tentativas. Sensor de impressão digital desativado."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Tente novamente."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -997,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Abrir"</string>
<string name="sms" msgid="4560537514610063430">"Mensagem"</string>
<string name="add_contact" msgid="7867066569670597203">"Adicionar"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Ver"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Programar"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Rastrear"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1128,8 +1127,7 @@
<item quantity="other">Abrir redes Wi-Fi disponíveis</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Conectar-se a uma rede Wi‑Fi aberta"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Conectar à rede Wi‑Fi da operadora"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Conectando-se a uma rede Wi‑Fi aberta"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Conectado a uma rede Wi‑Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Não foi possível conectar-se à rede Wi‑Fi"</string>
@@ -1213,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"isso pode lhe custar dinheiro"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB carregando este dispositivo"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB fornecendo energia ao dispositivo conectado"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferência de arquivos"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferência de fotos"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Toque para ver mais opções."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Acessório de áudio analógico detectado"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
@@ -1658,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Trabalho: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Segundo <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Terceiro <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Para liberar esta tela, mantenha os botões Voltar e Visão geral pressionados"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Tela fixada"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Tela liberada"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pedir PIN antes de liberar"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Pedir padrão de desbloqueio antes de liberar"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Pedir senha antes de liberar"</string>
@@ -1757,10 +1762,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Ativar o perfil de trabalho?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Seus apps, notificações, dados e outros recursos do perfil de trabalho serão ativados"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Ativar"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Este app foi criado para uma versão mais antiga do Android e pode não funcionar corretamente. Tente verificar se há atualizações ou entre em contato com o desenvolvedor."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Procurar atualizações"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Você tem mensagens novas"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Abra o app de SMS para ver"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Algumas funcionalidades são limitadas"</string>
@@ -1827,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Smartphone não autorizado para voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Esse atalho requer o app mais recente"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque o app não é compatível com backup e restauração"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido à incompatibilidade de assinatura de apps"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DESINSTALAR"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ABRIR MESMO ASSIM"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"App nocivo detectado"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> quer mostrar partes do app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Editar"</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 52995ae..defb40b 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -420,10 +420,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcțiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"să direcționeze apelurile prin intermediul sistemului"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"să continue un apel dintr-o altă aplicație"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Permite aplicației să continue un apel care a fost inițiat dintr-o altă aplicație."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"să citească numerele de telefon"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Permite aplicației să acceseze numerele de telefon ale dispozitivului."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"împiedicarea computerului tablet PC să intre în repaus"</string>
@@ -502,6 +500,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Prea multe încercări. Încercați din nou mai târziu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Prea multe încercări. Senzorul de amprentă este dezactivat."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Încercați din nou."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1017,12 +1019,9 @@
<string name="browse" msgid="1245903488306147205">"Deschideți"</string>
<string name="sms" msgid="4560537514610063430">"Mesaj"</string>
<string name="add_contact" msgid="7867066569670597203">"Adăugați"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Afișați"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Program"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Urmăriți"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spațiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
@@ -1150,8 +1149,7 @@
<item quantity="one">Rețea Wi-Fi deschisă disponibilă</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Conectați-vă la o rețea Wi‑Fi deschisă"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Conectați-vă la rețeaua Wi-Fi a operatorului"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Se stabilește conexiunea la o rețea Wi‑Fi deschisă"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"S-a realizat conexiunea la rețeaua Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Nu s-a putut stabili conexiunea la rețeaua Wi-Fi"</string>
@@ -1235,13 +1233,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Nu se solicită nicio permisiune"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"aceasta poate să genereze costuri"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Dispozitivul se încarcă prin USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Dispozitivul atașat se încarcă prin USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"Conexiune USB pentru transferul fișierelor"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"Conexiune USB pentru transferul fotografiilor"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"Conexiune USB pentru MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectat la un accesoriu USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Atingeți pentru mai multe opțiuni."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"S-a detectat un accesoriu audio analogic"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Dispozitivul atașat nu este compatibil cu acest telefon. Atingeți pentru a afla mai multe."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Remedierea erorilor prin USB este conectată"</string>
@@ -1683,9 +1691,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de serviciu"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> pentru serviciu (2)"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> pentru serviciu (3)"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Pentru a anula fixarea acestui ecran, atingeți lung butoanele Înapoi și Recente"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Ecran fixat"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Fixarea ecranului anulată"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Solicită codul PIN înainte de a anula fixarea"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Solicită mai întâi modelul pentru deblocare"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Solicită parola înainte de a anula fixarea"</string>
@@ -1791,10 +1796,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"Activați profilul de serviciu?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"Se vor activa aplicațiile dvs. de serviciu, notificările, datele și alte funcții ale profilului de serviciu"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Activați"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Această aplicație a fost creată pentru o versiune Android mai veche și este posibil să nu funcționeze corect. Încercați să căutați actualizări sau contactați dezvoltatorul."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Căutați actualizări"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Aveți mesaje noi"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Deschideți aplicația pentru SMS-uri ca să vizualizați"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Unele funcții ar putea fi limitate"</string>
@@ -1862,17 +1865,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefonul nu este permis pentru voce"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fereastră pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Această comandă rapidă necesită cea mai recentă aplicație"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nu s-a putut restabili comanda rapidă deoarece aplicația nu acceptă backupul și restabilirea"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nu s-a putut restabili comanda rapidă din cauza nepotrivirii semnăturii aplicației"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nu s-a putut restabili comanda rapidă"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Comanda rapidă este dezactivată"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DEZINSTALAȚI"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"Deschideți oricum"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Aplicație dăunătoare detectată"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> vrea să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Editați"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0d62e75..817cbf7 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -270,10 +270,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасный режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Переключиться на личный профиль"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Переключиться на рабочий профиль"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакты"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"доступ к контактам"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Разрешите приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к контактам"</string>
@@ -425,10 +423,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Приложение получит доступ к функциям телефона на устройстве. Кроме того, оно сможет определять номера телефонов и серийные номера моделей, состояние активности вызова, а также удаленные номера, с которыми установлено соединение."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"перенаправлять звонки в системе"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Приложение сможет перенаправлять звонки в системе с целью улучшения качества связи."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"Продолжить вызов, начатый в другом приложении"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Разрешает приложению продолжить вызов, начатый в другом приложении."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"чтение номеров телефонов"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Разрешает приложению доступ к телефонным номерам устройства."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"Отключение спящего режима"</string>
@@ -503,11 +499,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Чтобы сохранить новый отпечаток, удалите существующий."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Превышено время ожидания. Повторите попытку."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Операция с отпечатком отменена."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Операция с отпечатком пальца отменена пользователем."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Слишком много попыток. Повторите позже."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Слишком много попыток. Сканер отпечатков пальцев отключен."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Повторите попытку."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1040,12 +1039,9 @@
<string name="browse" msgid="1245903488306147205">"Открыть"</string>
<string name="sms" msgid="4560537514610063430">"Написать SMS"</string>
<string name="add_contact" msgid="7867066569670597203">"Добавить"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Открыть"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Расписание"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Отследить"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Недостаточно памяти"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые функции могут не работать"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1175,8 +1171,7 @@
<item quantity="other">Есть открытые сети Wi-Fi</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Подключитесь к открытой сети Wi‑Fi"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Подключитесь к сети Wi‑Fi оператора"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Подключение к открытой сети Wi‑Fi…"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Подключено к сети Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Не удалось подключиться к сети Wi‑Fi"</string>
@@ -1260,13 +1255,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Не требуется разрешений"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"это может стоить вам денег!"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ОК"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Зарядка через USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Подача питания на подключенное устройство через USB"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"Передача файлов через USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"Передача фото через USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI через USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB-устройство подключено"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Нажмите, чтобы показать дополнительные параметры."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Обнаружено аналоговое аудиоустройство"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Подсоединенное устройство несовместимо с этим телефоном. Нажмите, чтобы узнать подробности."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отладка по USB разрешена"</string>
@@ -1711,9 +1716,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Рабочий <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Задача 2: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Задача 3: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Чтобы открепить экран, нажмите и удерживайте кнопки \"Назад\" и \"Обзор\""</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Блокировка включена"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Блокировка выключена"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"PIN-код для отключения"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Запрашивать графический ключ для отключения блокировки"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Запрашивать пароль для отключения блокировки"</string>
@@ -1825,15 +1827,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Все языки"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Все регионы"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Поиск"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Включить рабочий профиль?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Будут включены корпоративные приложения, уведомления, данные и другие функции рабочего профиля."</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Включить"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Это приложение было создано для более ранней версии Android и может работать со сбоями. Проверьте наличие обновлений или свяжитесь с разработчиком."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Проверить обновления"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Новые сообщения"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Чтобы просмотреть, откройте приложение для обмена SMS"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Некоторые функции недоступны"</string>
@@ -1902,17 +1900,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Для телефона недоступны голосовые вызовы"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Всплывающее окно"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Требуется последняя версия приложения"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не удалось восстановить ярлык: приложение не поддерживает резервное копирование"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не удалось восстановить ярлык: некорректная подпись приложения"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не удалось восстановить ярлык"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Ярлык отключен"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"УДАЛИТЬ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ОТКРЫТЬ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Обнаружено вредоносное приложение"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"Приложение \"<xliff:g id="APP_0">%1$s</xliff:g>\" запрашивает разрешение на показ фрагментов приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"."</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Изменить"</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 846b0ee..ab69242 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -417,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"උපාංගයේ දුරකථන විශේෂාංග වෙත ප්රවේශයට යෙදුමට ඉඩ දෙයි. ඇමතුම සක්රිය වුවත්, සහ ඇමතුමකින් දුරස්ථ අංකය සම්බන්ධ වුවත් දුරකථන අංකය සහ උපාංග ID හඳුනා ගැනීමට මෙම අවසරය යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"පද්ධතිය හරහා ඇමතුම් මාර්ගගත කරන්න"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"ඇමතුම් අත්දැකීම වැඩිදියුණු කිරීම සඳහා යෙදුමට පද්ධතිය හරහා එහි ඇමතුම් මාර්ගගත කිරීමට ඉඩ දෙයි."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"වෙනත් යෙදුමක් වෙතින් වන ඇමතුමක් දිගටම කරගෙන යන්න"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"වෙනත් යෙදුමක ආරම්භ කරන ලද ඇමතුමක් දිගටම කරගෙන යාමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"දුරකථන අංක කියවන්න"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"උපාංගයේ දුරකථන අංක වෙත ප්රවේශයට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ටැබ්ලටය නින්දෙන් වැළක්වීම"</string>
@@ -499,6 +497,10 @@
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"උත්සාහයන් ඉතා වැඩි ගණනකි. කරුණාකර පසුව නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"උත්සාහයන් ඉතා වැඩි ගණනකි. ඇඟිලි සලකුණු සංවේදකය අබල කරන ලදී."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"නැවත උත්සාහ කරන්න."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -999,12 +1001,9 @@
<string name="browse" msgid="1245903488306147205">"විවෘත කරන්න"</string>
<string name="sms" msgid="4560537514610063430">"පණිවිඩය"</string>
<string name="add_contact" msgid="7867066569670597203">"එක් කරන්න"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"බලන්න"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"කාල සටහන"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ශ්රව්ය ඛණ්ඩය"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ආචයනය ඉඩ ප්රමාණය අඩු වී ඇත"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"සමහර පද්ධති කාර්යයන් ක්රියා නොකරනු ඇත"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"පද්ධතිය සඳහා ප්රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -1130,8 +1129,7 @@
<item quantity="other">විවෘත Wi-Fi ජාල තිබේ</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"විවෘත Wi-Fi ජාලය වෙත සම්බන්ධ වෙන්න"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"වාහක Wi-Fi ජාලයට සම්බන්ධ වන්න"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"විවෘත Wi-Fi ජාලය වෙත සම්බන්ධ වෙමින්"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Wi-Fi ජාලයක් වෙත සම්බන්ධ විය"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi-Fi ජාලයක් වෙත සම්බන්ධ විය නොහැකි විය"</string>
@@ -1215,13 +1213,23 @@
<string name="no_permissions" msgid="7283357728219338112">"අවසර අවශ්ය නොමැත"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"මෙමඟින් ඔබට මුදල් වැය විය හැක"</string>
<string name="dlg_ok" msgid="7376953167039865701">"හරි"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"මෙම උපාංගය USB වෙතින් ආරෝපණය"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"අමුණා ඇති උපාංගයට USB මඟින් බලය සපයමින්"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ගොනු හුවමාරුව සඳහා USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ඡායාරූප හුවමාරුව සඳහා USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI සඳහා USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB මෙවලමකට සම්බන්ධිතයි"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"තවත් විකල්ප සඳහා තට්ටු කරන්න."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"ප්රතිසම ශ්රව්ය උපාංගය අනාවරණය කර ගන්නා ලදී"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"මෙම දුරකථනය සමඟ සම්බන්ධිත උපාංගය නොගැළපෙයි. තව දැන ගැනීමට තට්ටු කරන්න."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB නිදොස්කරණය සම්බන්ධිතයි"</string>
@@ -1660,9 +1668,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"වැඩ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2වන වැඩ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3වන වැඩ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"මෙම තිර ඇමුණුම ගැලවීමට, දළ විශ්ලේෂණය බොත්තම් ස්පර්ශ කර අල්ලා ගෙන සිටින්න"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"තිරය අගුළු දමා ඇත"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"තිරයේ අගුළු ඇර ඇත"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ගැලවීමට පෙර PIN විමසන්න"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"ගැලවීමට පෙර අගුළු අරින රටාව සඳහා අසන්න"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"ගැලවීමට පෙර මුරපදය විමසන්න"</string>
@@ -1759,10 +1764,8 @@
<string name="work_mode_off_title" msgid="1118691887588435530">"කාර්යාල පැතිකඩ ක්රියාත්මක කරන්නද?"</string>
<string name="work_mode_off_message" msgid="5130856710614337649">"ඔබගේ වැඩ යෙදුම්, දැනුම්දීම්, දත්ත සහ වෙනත් කාර්යාල පැතිකඩ විශේෂාංග ක්රියාත්මක කරනු ඇත"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ක්රියාත්මක කරන්න"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"මෙම යෙදුම Android හි පැරණි අනුවාදයක් සඳහා තනා ඇති අතර නිසියාකාරව ක්රියා නොකරනු ඇත. යාවත්කාලීන සඳහා පරික්ෂා කිරීම උත්සාහ කරන්න, නැතහොත් සංවර්ධක අමතන්න."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"යාවත්කාලීන සඳහා පරික්ෂා කරන්න"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"ඔබට නව පණිවිඩ තිබේ"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"බැලීමට විවෘත SMS යෙදුම විවෘත කරන්න"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"ඇතැම් ක්රියාකාරිත්ව සීමිත විය හැකිය"</string>
@@ -1829,17 +1832,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"දුරකථනය හඬ සඳහා ඉඩ නොදේ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"උත්පතන කවුළුව"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"මෙම කෙටි මගට නවතම යෙදුම අවශ්යයි"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"යෙදුම උපස්ථ සහ ප්රතිසාධනය සඳහා සහාය නොදක්වන බැවින් කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"යෙදුම් අත්සන නොගැළපෙන බැවින් කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"කෙටි මග අබල කර ඇත"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"අස්ථාපනය කරන්න"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"කෙසේ වුවත් විවෘත කරන්න"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"හානිකර යෙදුමක් අනාවරණය කර ගන්නා ලදී"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට අවශ්යයි"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"සංස්කරණය"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 32d91ea..66baaf4 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -262,10 +262,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Tumia wasifu wa binafsi"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Tumia wasifu wa kazini"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Anwani"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ifikie anwani zako"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Ruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie anwani zako"</string>
@@ -417,10 +415,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Huruhusu programu kufikia vipengele vya simu vilivyo kwenye kifaa. Idhini hii inaruhusu programu kutambua nambari ya simu na kifaa, kama kuna simu inayopigwa, na nambari ya mbali iliyounganishwa kwenye simu."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"elekeza simu kupitia mfumo"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Huruhusu programu kuelekeza simu zake kupitia mfumo ili kuboresha hali ya kupiga simu."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"endelea na simu kutoka programu nyingine"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Huruhusu programu kuendelea na simu ambayo ilianzishwa katika programu nyingine."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"kusoma nambari za simu"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Inaruhusu programu kufikia nambari za simu zilizo kwenye kifaa."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"zuia kompyuta ndogo dhidi ya kulala"</string>
@@ -495,11 +491,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Kitambulisho hakiwezi kuhifadhiwa. Tafadhali ondoa kitambulisho kilichopo."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Muda wa kitambulisho umekwisha. Jaribu tena."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Utendaji wa kitambulisho imeghairiwa."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Majaribio mengi mno. Jaribu tena baadaye."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Majaribio mengi mno. Kitambua alama ya kidole kimezimwa."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Jaribu tena."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Kitambulisho <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -998,12 +997,9 @@
<string name="browse" msgid="1245903488306147205">"Fungua"</string>
<string name="sms" msgid="4560537514610063430">"Ujumbe"</string>
<string name="add_contact" msgid="7867066569670597203">"Ongeza"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Angalia"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Ratiba"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Toleo"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -1129,8 +1125,7 @@
<item quantity="one">Fungua mtandao wa Wi-Fi unaopatikana</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Unganisha kwenye mtandao wa Wi‑Fi unaotumiwa na mtu yeyote"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Unganisha kwenye mtandao wa mtoa huduma ya Wi‑Fi"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Inaunganisha kwenye mtandao wa Wi‑Fi unaotumiwa na mtu yeyote"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Imeunganisha kwenye mtandao wa Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Imeshindwa kuunganisha kwenye mtandao wa Wi‑Fi"</string>
@@ -1214,13 +1209,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Hakuna vibali vinavyohitajika"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"huenda hii ikakugharimu pesa"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Sawa"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Unachaji kifaa hiki ukitumia USB"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB inachaji kifaa ulichounganisha"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB kwa ajili ya kuhamisha faili"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB kwa ajili ya kuhamisha picha"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Imeunganishwa kwa kifuasi cha USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Gusa ili upate chaguo zaidi."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Imetambua kifaa cha sauti ya analogi"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gusa ili upate maelezo zaidi."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
@@ -1659,9 +1664,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Ya kazini <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> ya 2 ya Kazini"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> ya 3 ya Kazini"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Ili kubandua skrini hii, gusa na ushikilie vitufe vya Nyuma na Muhtasari"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Skrini imebandikwa"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Skrini imebanduliwa"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Itisha PIN kabla hujabandua"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Omba mchoro wa kufungua kabla hujabandua"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Omba nenosiri kabla hujabandua"</string>
@@ -1755,15 +1757,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Lugha zote"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Maeneo yote"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Tafuta"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Ungependa kuwasha wasifu wa kazini?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Hatua hii itawasha data, arifa, programu za kazini, arifa na vipengele vingine vya wasifu wa kazini"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Washa"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Programu hii iliundwa kwa ajili ya toleo la zamani la Android na huenda isifanye kazi vizuri. Jaribu kuangalia masasisho au uwasiliane na msanidi programu."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Angalia masasisho"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Una ujumbe mpya"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Fungua programu ya SMS ili uweze kuangalia"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Huenda baadhi ya vipengele havifanyi kazi"</string>
@@ -1830,17 +1828,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Simu hairuhusiwi katika huduma ya sauti"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Dirisha Ibukizi"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Njia hii ya mkato inahitaji toleo jipya la programu"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Imeshindwa kurejesha njia ya mkato kwa sababu programu haitumii kipengele cha hifadhi rudufu na kurejesha upya"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Imeshindwa kurejesha njia ya mkato kwa sababu ufunguo wako wa kuambatisha cheti kwenye programu haulingani"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Imeshindwa kurejesha njia ya mkato"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Njia ya mkato imezimwa"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ONDOA"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"FUNGUA TU"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Imetambua programu hatari"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> inataka kuonyesha vipengee <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Badilisha"</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 3afdebd..5afa711 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"பாதுகாப்பு பயன்முறை"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android அமைப்பு"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"தனிப்பட்ட சுயவிவரத்திற்கு மாற்றவும்"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"பணிச் சுயவிவரத்திற்கு மாற்றவும்"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"தொடர்புகள்"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"தொடர்புகளை அணுக வேண்டும்"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"தொடர்புகளை அணுக, <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>க்கு அனுமதி வழங்கவும்"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"சாதனத்தின் மொபைல் அம்சங்களை அணுகப் பயன்பாட்டை அனுமதிக்கிறது. மொபைல் மற்றும் சாதன ஐடிகள், அழைப்பு செயலில் உள்ளதா மற்றும் அழைப்பு மூலம் இணைக்கப்பட்ட தொலைக் கட்டுப்பாட்டு எண் ஆகியவற்றைத் தீர்மானிக்க இந்த அனுமதி பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"சிஸ்டம் மூலம் அழைப்புகளை ரூட் செய்தல்"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"அழைக்கும் அனுபவத்தை மேம்படுத்தும் பொருட்டு, சிஸ்டம் மூலம் தனது அழைப்புகளை ரூட் செய்ய பயன்பாட்டை அனுமதிக்கும்."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"மற்றொரு பயன்பாட்டிலிருந்து அழைப்பைத் தொடருதல்"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"மற்றொரு பயன்பாட்டில் தொடங்கப்பட்ட அழைப்பைத் தொடருவதற்கு, பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ஃபோன் எண்களைப் படித்தல்"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"சாதனத்தின் ஃபோன் எண்களை அணுக, பயன்பாட்டை அனுமதிக்கும்."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"டேப்லெட் உறக்க நிலைக்குச் செல்வதைத் தடுத்தல்"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"கைரேகையைச் சேமிக்க முடியவில்லை. ஏற்கனவே உள்ள கைரேகையை அகற்றவும்."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"கைரேகை செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"பயனர், கைரேகை உறுதிப்படுத்துதலை ரத்துசெய்தார்."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"அதிகமான முயற்சிகள். பிறகு முயற்சிக்கவும்."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"பலமுறை முயன்றுவிட்டீர்கள். கைரேகை சென்சார் முடக்கப்பட்டது."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"மீண்டும் முயற்சிக்கவும்."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"திற"</string>
<string name="sms" msgid="4560537514610063430">"செய்தி"</string>
<string name="add_contact" msgid="7867066569670597203">"சேர்"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"காண்பி"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"திட்டமிடுதல்"</string>
+ <string name="view_flight" msgid="7691640491425680214">"கண்கானி"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"சேமிப்பிடம் குறைகிறது"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">பொது வைஃபை நெட்வொர்க் உள்ளது</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"திறந்த வைஃபை நெட்வொர்க்குடன் இணைக்கவும்"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"தொலைத்தொடர்பு சேவை வழங்கும் நிறுவனத்தின் வைஃபை நெட்வொர்க்குடன் இணைக்கிறது"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"திறந்த வைஃபை நெட்வொர்க்குடன் இணைக்கிறது"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"வைஃபை நெட்வொர்க்குடன் இணைக்கப்பட்டது"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"வைஃபை நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"அனுமதிகள் தேவையில்லை"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"இதனால் நீங்கள் கட்டணம் செலுத்த வேண்டியிருக்கலாம்"</string>
<string name="dlg_ok" msgid="7376953167039865701">"சரி"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"சாதனம் USB சார்ஜிங் செய்யப்படுகிறது"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"இணைத்துள்ள சாதனத்திற்கு USB சக்தி அளிக்கிறது"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB, கோப்புப் பரிமாற்றத்துக்கு மட்டும்"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB, படப் பரிமாற்றத்துக்கு மட்டும்"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB, MIDIக்கு மட்டும்"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB துணைக்கருவியுடன் இணைக்கப்பட்டுள்ளது"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"மேலும் விருப்பங்களுக்கு, தட்டவும்."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"அனலாக் ஆடியோ துணைக்கருவி கண்டறியப்பட்டது"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"இணைத்துள்ள சாதனமானது இந்த மொபைலுடன் இணங்கவில்லை. மேலும் அறிய, தட்டவும்."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB பிழைத் திருத்தம் இணைக்கப்பட்டது"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"பணியிடம் <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2வது பணி <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3வது பணி <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"இந்தத் திரையை அகற்ற, முந்தையது, மேலோட்டப் பார்வை ஆகிய இரண்டு பொத்தானையும் தொட்டுப் பிடித்திருக்கவும்"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"திரை பின் செய்யப்பட்டது"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"திரையின் பின் அகற்றப்பட்டது"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"அகற்றும் முன் PINஐக் கேள்"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"அகற்றும் முன் திறத்தல் வடிவத்தைக் கேள்"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"அகற்றும் முன் கடவுச்சொல்லைக் கேள்"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"எல்லா மொழிகளும்"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"எல்லா மண்டலங்களும்"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"தேடு"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"பணிச் சுயவிவரத்தை ஆன் செய்யவா?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"பணி ஆப்ஸ், அறிவிப்புகள், தரவு மற்றும் பிற பணிச் சுயவிவர அம்சங்கள் ஆன் செய்யப்படும்"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"இயக்கு"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"இந்தப் பயன்பாடு Android இன் பழைய பதிப்புக்காக உருவாக்கப்பட்டதால், சரியாக வேலை செய்யாமல் போகலாம். புதுப்பிப்புகள் ஏதேனும் உள்ளதா எனப் பார்க்கவும் அல்லது டெவெலப்பரைத் தொடர்புகொள்ளவும்."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"புதுப்பிப்பு உள்ளதா எனப் பார்"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"புதிய செய்திகள் வந்துள்ளன"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"பார்க்க, SMS பயன்பாட்டைத் திறக்கவும்"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"சில செயல்பாடு வரம்பிடப்பட்டிருக்கலாம்"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"குரல் அழைப்பை மேற்கொள்ள இந்த ஃபோனுக்கு அனுமதி இல்லை"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"பாப்அப் சாளரம்"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"இந்த ஷார்ட்கட்டைப் பயன்படுத்த, சமீபத்திய பயன்பாடு வேண்டும்"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"காப்புப் பிரதி மற்றும் மீட்டமைவைப் பயன்பாடு ஆதரிக்காத காரணத்தால், ஷார்ட்கட்டை மீட்டமைக்க முடியவில்லை"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"பயன்பாட்டுச் சான்றுகள் பொருந்தாத காரணத்தினால், ஷார்ட்கட்டை மீட்டமைக்க முடியவில்லை"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ஷார்ட்கட்டை மீட்டமைக்க முடியவில்லை"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"குறுக்குவழி முடக்கப்பட்டுள்ளது"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"நிறுவல் நீக்கு"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"பரவாயில்லை, திற"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"தீங்கிழைக்கும் பயன்பாடு உள்ளது"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_2">%2$s</xliff:g> பயன்பாட்டின் விழிப்பூட்டல்களைக் காண்பிக்க, <xliff:g id="APP_0">%1$s</xliff:g> அனுமதி கேட்கிறது"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"திருத்து"</string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 183b3a2..a08c483 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"సురక్షిత మోడ్"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android సిస్టమ్"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"వ్యక్తిగత ప్రొఫైల్కి మార్చు"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"కార్యాలయ ప్రొఫైల్కి మార్చు"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"పరిచయాలు"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"మీ పరిచయాలను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని ఉపయోగించి, మీ పరిచయాలను యాక్సెస్ చేయండి"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"పరికరం యొక్క ఫోన్ ఫీచర్లను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్ను కనుగొనడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"కాల్లను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్లను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"మరో యాప్ నుండి కాల్ని కొనసాగించండి"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"మరో యాప్లో ప్రారంభించిన కాల్ని కొనసాగించడానికి యాప్ని అనుమతిస్తుంది."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"ఫోన్ నంబర్లను చదువు"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"పరికరం యొక్క ఫోన్ నంబర్లను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"టాబ్లెట్ను నిద్రావస్థకు వెళ్లనీయకుండా నిరోధించడం"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"వేలిముద్రను నిల్వ చేయడం సాధ్యపడదు. దయచేసి ఇప్పటికే ఉన్న వేలిముద్రను తీసివేయండి."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"వేలిముద్ర కార్యకలాపం రద్దయింది."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేసారు."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"అనేకసార్లు ప్రయత్నించారు. వేలిముద్ర సెన్సార్ నిలిపివేయబడింది."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"మళ్లీ ప్రయత్నించండి."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"వేలు <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"తెరవండి"</string>
<string name="sms" msgid="4560537514610063430">"సందేశం"</string>
<string name="add_contact" msgid="7867066569670597203">"జోడించండి"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"చూడండి"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"షెడ్యూల్"</string>
+ <string name="view_flight" msgid="7691640491425680214">"ట్రాక్ చేయండి"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">ఓపెన్ Wi-Fi నెట్వర్క్ అందుబాటులో ఉంది</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"బహిరంగ Wi‑Fi నెట్వర్క్కు కనెక్ట్ చేయండి"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"క్యారియర్ Wi‑Fi నెట్వర్క్కి కనెక్ట్ చేయండి"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"బహిరంగ Wi‑Fi నెట్వర్క్కు కనెక్ట్ చేస్తోంది"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Wi‑Fi నెట్వర్క్కు కనెక్ట్ చేయబడింది"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi‑Fi నెట్వర్క్కు కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"అనుమతులు అవసరం లేదు"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"దీనికి మీకు డబ్బు ఖర్చు కావచ్చు"</string>
<string name="dlg_ok" msgid="7376953167039865701">"సరే"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"ఈ పరికరం USB మోడ్లో ఛార్జ్ అవుతోంది"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"జోడించిన పరికరానికి USB ద్వారా పవర్ సరఫరా అవుతోంది"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ఫైల్ బదిలీ కోసం USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ఫోటో బదిలీ కోసం USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI కోసం USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ఉపకరణానికి కనెక్ట్ చేయబడింది"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"మరిన్ని ఎంపికల కోసం నొక్కండి."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"అనలాగ్ ఆడియో ఉపకరణం కనుగొనబడింది"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"జోడించిన పరికరం ఈ ఫోన్కు అనుకూలంగా లేదు. మరింత తెలుసుకోవడానికి నొక్కండి."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB డీబగ్గింగ్ కనెక్ట్ చేయబడింది"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"కార్యాలయం <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2వ కార్యాలయం <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3వ కార్యాలయం <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"ఈ స్క్రీన్ను అన్పిన్ చేయడానికి, వెనుకకు మరియు స్థూలదృష్టి బటన్లను నొక్కి & పట్టుకోండి"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"స్క్రీన్ పిన్ చేయబడింది"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"స్క్రీన్ అన్పిన్ చేయబడింది"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"అన్పిన్ చేయడానికి ముందు పిన్ కోసం అడుగు"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"అన్పిన్ చేయడానికి ముందు అన్లాక్ ఆకృతి కోసం అడుగు"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"అన్పిన్ చేయడానికి ముందు పాస్వర్డ్ కోసం అడుగు"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"అన్ని భాషలు"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"అన్ని ప్రాంతాలు"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"వెతుకు"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"కార్యాలయ ప్రొఫైల్ని ఆన్ చేయాలా?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"మీ కార్యాలయ యాప్లు, నోటిఫికేషన్లు, డేటా మరియు ఇతర కార్యాలయ ప్రొఫైల్ ఫీచర్లు ఆన్ చేయబడతాయి"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"ఆన్ చేయి"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్డేట్ల కోసం తనిఖీ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్ని సంప్రదించండి."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"అప్డేట్ కోసం తనిఖీ చేయండి"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"మీకు కొత్త సందేశాలు ఉన్నాయి"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"వీక్షించడానికి SMS అనువర్తనాన్ని తెరవండి"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"కొంత పనితనం పరిమితం కావచ్చు"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"వాయిస్ కోసం ఫోన్ అనుమతించబడదు"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"పాప్అప్ విండో"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ఈ సత్వరమార్గానికి తాజా యాప్ అవసరం"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"యాప్ సంతకం సరిపోలని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"షార్ట్కట్ నిలిపివేయబడింది"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"అన్ఇన్స్టాల్ చేయండి"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ఏదేమైనా తెరువు"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"హానికరమైన యాప్ గుర్తించబడింది"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> స్లైస్లను చూపించాలనుకుంటోంది"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"సవరించు"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 929991f..9fea248 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Kişisel profile geçiş yap"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"İş profiline geç"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kişiler"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kişilerinize erişme"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının kişilerinize erişmesine izin verin"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Uygulamaya cihazdaki telefon özelliklerine erişme izni verir. Bu izin, uygulamanın telefon numarasını ve cihaz kimliğini, etkin bir çağrı olup olmadığını ve çağrıda bağlanılan karşı tarafın numarasını öğrenmesine olanak sağlar."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"çağrıları sistem üzerinden yönlendir"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Uygulamanın, çağrı deneyimini iyileştirmek için çağrılarını sistem üzerinden yönlendirmesine olanak tanır."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"başka bir uygulamadaki çağrıya devam etme"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Uygulamanın, başka bir uygulamada başlatılan çağrıya devam etmesine izin verir."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon numaralarını oku"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Uygulamaya, cihazınızın telefon numaralarına erişme izni verir."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"tabletin uykuya geçmesini önle"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Parmak izi işlemi iptal edildi."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Çok fazla deneme yapıldı. Parmak izi sensörü devre dışı bıraıldı."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Tekrar deneyin."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Aç"</string>
<string name="sms" msgid="4560537514610063430">"Mesaj"</string>
<string name="add_contact" msgid="7867066569670597203">"Ekle"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Göster"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Program yap"</string>
+ <string name="view_flight" msgid="7691640491425680214">"İzle"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Kullanılabilir Kablosuz ağı aç</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Açık kablosuz ağa bağlanın"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Operatöre ait kablosuz ağa bağlanın"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Açık kablosuz ağa bağlandı"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Kablosuz ağa bağlanıldı"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Kablosuz ağa bağlanamadı"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"bunun için sizden ücret alınabilir"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Tamam"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"Bu cihaz USB\'den şarj oluyor"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB, bağlı cihaza güç sağlıyor"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"Dosya aktarımı için USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"Fotoğraf aktarımı için USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI için USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB aksesuarına bağlandı"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Diğer seçenekler için dokunun."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analog ses aksesuarı algılandı"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Takılan cihaz bu telefonla uyumlu değil. Daha fazla bilgi edinmek için dokunun."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB hata ayıklaması bağlandı"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (İş)"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"İş için 2. <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"İş için 3. <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Bu ekranın sabitlemesini kaldırmak için Geri\'ye ve Genel Bakış\'a dokunup basılı tutun"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Ekran sabitlendi"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran sabitlemesi kaldırıldı"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Sabitlemeyi kaldırmadan önce PIN\'i sor"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Sabitlemeyi kaldırmadan önce kilit açma desenini sor"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Sabitlemeyi kaldırmadan önce şifre sor"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Tüm diller"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Tüm bölgeler"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Ara"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"İş profili açılsın mı?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"İş uygulamalarınız, bildirimleriniz, verileriniz ve diğer iş profili özellikleriniz açılacak"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Aç"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Bu uygulama Android\'in daha eski bir sürümü için oluşturuldu ve düzgün çalışmayabilir. Güncellemeleri kontrol etmeyi deneyin veya geliştiriciyle iletişime geçin."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Güncellemeleri denetle"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Yeni mesajlarınız var"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Görüntülemek için SMS uygulamasını açın"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Bazı işlevler sınırlı olabilir"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Ses için telefona izin verilmiyor"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-up Pencere"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Bu kısayol, en son uygulamayı gerektiriyor"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Uygulama, yedekleme ve geri yüklemeyi desteklemediğinden kısayol geri yüklenemedi"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Uygulama imzası eşleşmediğinden kısayol geri yüklenemedi"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kısayol geri yüklenemedi"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Kısayol devre dışı"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"YÜKLEMEYİ KALDIR"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"YİNE DE AÇ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Zararlı uygulama tespit edildi"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> uygulaması, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermek istiyor"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Düzenle"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b028b36..4ed3790 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -270,10 +270,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Безпечний режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Перейти в особистий профіль"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Перейти в робочий профіль"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"отримувати доступ до контактів"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Надати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до контактів"</string>
@@ -425,10 +423,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дозволяє програмі отримувати доступ до телефонних функцій пристрою. Такий дозвіл дає програмі змогу визначати номер телефону й ідентифікатори пристрою, активність виклику, а також віддалений номер, на який здійснюється виклик."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"маршрутизувати виклики через систему"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Дозволяє додатку маршрутизувати виклики через систему, щоб було зручніше телефонувати."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"продовжувати виклик з іншого додатка"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Додаток може продовжувати виклик, початий в іншому додатку."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"переглядати номери телефону"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Надає додаткам доступ до номерів телефону на пристрої."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"не доп.перехід пристр.в реж.сну"</string>
@@ -503,11 +499,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток. Видаліть наявний відбиток."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час очікування відбитка минув. Повторіть спробу."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Дію з відбитком скасовано."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Користувач скасував дію з відбитком пальця."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Забагато спроб. Спробуйте пізніше."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Забагато спроб. Сканер відбитків пальців вимкнено."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Повторіть спробу."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1040,12 +1039,9 @@
<string name="browse" msgid="1245903488306147205">"Відкрити"</string>
<string name="sms" msgid="4560537514610063430">"Повідомлення"</string>
<string name="add_contact" msgid="7867066569670597203">"Додати"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Переглянути"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Запланувати"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Відстежити"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1175,8 +1171,7 @@
<item quantity="other">Відкриті мережі Wi-Fi доступні</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Під’єднайтеся до відкритої мережі Wi-Fi"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Під’єднайтеся до мережі Wi-Fi оператора"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Під’єднання до відкритої мережі Wi-Fi"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Під’єднано до мережі Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Не вдалося під’єднатися до мережі Wi-Fi"</string>
@@ -1260,13 +1255,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Дозвіл не потрібний"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"це платна послуга"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB-кабель, через який заряджається цей пристрій"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB-кабель, через який живиться під’єднаний пристрій"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB для перенесення файлів"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB для перенесення фотографій"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для режиму MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Під’єднано до аксесуара USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Торкніться, щоб переглянути більше опцій."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Виявлено аналоговий аксесуар для аудіо"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Під’єднаний пристрій несумісний із цим телефоном. Торкніться, щоб дізнатися більше."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Налагодження USB завершено"</string>
@@ -1711,9 +1716,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Робоча <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2-а робота: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3-я робота: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Щоб відкріпити цей екран, натисніть і втримуйте кнопки \"Назад\" та \"Огляд\""</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Екран закріплено"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Екран відкріплено"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"PIN-код для відкріплення"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Запитувати ключ розблокування перед відкріпленням"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Запитувати пароль перед відкріпленням"</string>
@@ -1825,15 +1827,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Усі мови"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Усі регіони"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Пошук"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Увімкнути робочий профіль?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Додатки, сповіщення, дані й інші функції робочого профілю буде ввімкнено"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Увімкнути"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Цей додаток створений для старішої версії Android і може працювати неналежним чином. Спробуйте знайти оновлення або зв’яжіться з розробником."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Шукати оновлення"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"У вас є нові повідомлення"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Щоб переглянути, відкрийте додаток для SMS"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Деякі функції можуть не працювати"</string>
@@ -1902,17 +1900,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Голосові дзвінки з телефона заборонено"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Спливаюче вікно"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Для цього ярлика потрібна найновіша версія додатка"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не вдалося відновити ярлик, оскільки додаток не підтримує резервне копіювання та відновлення"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не вдалося відновити ярлик, оскільки підписи додатків не збігаються"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не вдалося відновити ярлик"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Ярлик вимкнено"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"ВИДАЛИТИ"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"УСЕ ОДНО ВІДКРИТИ"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Виявлено шкідливий додаток"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> хоче показати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Редагувати"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index c241214..bfaaba2 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>، <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"حفاظتی وضع"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android سسٹم"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"ذاتی پروفائل پر سوئچ کریں"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"دفتری پروفائل پر سوئچ کریں"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"رابطے"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"اپنے رابطوں تک رسائی حاصل کریں"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو اپنے رابطوں تک رسائی کی اجازت دیں"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"ایپ کو آلے کی فون والی خصوصیات تک رسائی حاصل کرنے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو فون نمبر اور آلے کے IDs کا تعین کرنے، آیا کوئی کال فعال ہے، اور کال کے ذریعہ مربوط ریموٹ نمبر کا تعین کرنے دیتی ہے۔"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"سسٹم کے ذریعہ کالز روٹ کریں"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"کالںگ کا تجربہ بہتر بنانے کے لیے سسٹم کے ذریعہ ایپ کو کالز روٹ کرنے کی اجازت دیتا ہے۔"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"دوسری ایپ کی کال جاری رکھیں"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"ایپ کو دوسری ایپ میں شروع کردہ کال کو جاری رکھنے کی اجازت ملتی ہے۔"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"فون نمبرز پڑھیں"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"ایپ کو آلہ کے فون نمبرز تک رسائی کرنے دیتا ہے۔"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ٹیبلیٹ کو سلیپ وضع میں جانے سے روکیں"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"فنگر پرنٹ اسٹور نہیں کیا جا سکتا ہے۔ براہ کرم ایک موجودہ فنگر پرنٹ ہٹائیں۔"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"کافی زیادہ کوششیں کی گئیں۔ بعد میں دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"کافی زیادہ کوششیں۔ فنگر پرنٹ سینسر غیر فعال ہو گیا۔"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"دوبارہ کوشش کریں۔"</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"کھولیں"</string>
<string name="sms" msgid="4560537514610063430">"پیغام"</string>
<string name="add_contact" msgid="7867066569670597203">"شامل کریں"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"دیکھیں"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"شیڈول کریں"</string>
+ <string name="view_flight" msgid="7691640491425680214">"پتہ لگائیں"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">عوامی Wi-Fi نیٹ ورک دستیاب ہے</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"عوامی Wi‑Fi نیٹ ورک سے منسلک ہوں"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"کیریئر Wi‑Fi نیٹ ورک سے منسلک ہوں"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"عوامی Wi‑Fi نیٹ ورک سے منسلک ہو رہا ہے"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"عوامی Wi‑Fi نیٹ ورک سے منسلک ہو گيا"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi‑Fi نیٹ ورک سے منسلک نہیں ہو سکا"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"کوئی اجازتیں درکار نہیں ہیں"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"اس میں آپ کا پیسہ خرچ ہو سکتا ہے"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ٹھیک ہے"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB اس آلے کو چارج کر رہی ہے"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"منسلکہ آلے کو USB پاور سپلائی کر رہی ہے"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"فائل کی منتقلی کیلئے USB"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"تصویر کی منتقلی کیلئے USB"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI کیلئے USB"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ایک USB لوازم سے مربوط ہے"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"مزید اختیارات کیلئے تھپتھپائیں۔"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"اینالاگ آڈیو کے لوازم کا پتہ چلا"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"منسلک آلہ اس فون کے موافق نہیں ہے۔ مزید جاننے کے لیے تھپتھپائیں۔"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ڈیبگ کرنا مربوط ہو گیا"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"دفتر <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"دوسرا کام <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"تیسرا کام <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"اس اسکرین سے پن ہٹانے کیلئے، ٹچ کریں اور مجموعی جائزہ اور واپس جائیں بٹنز کو دبائے رکھیں"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"اسکرین کو پن کر دیا گیا"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"اسکرین کا پن ہٹا دیا گیا"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"پن ہٹانے سے پہلے PIN طلب کریں"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"پن ہٹانے سے پہلے غیر مقفل کرنے کا پیٹرن طلب کریں"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"پن ہٹانے سے پہلے پاس ورڈ طلب کریں"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"سبھی زبانیں"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"تمام علاقے"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"تلاش"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"دفتری پروفائل آن کریں؟"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"آپ کی دفتری ایپس، اطلاعات، ڈیٹا اور دفتری پروفائل کی دیگر خصوصیات آن کر دی جائیں گی"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"آن کریں"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈیولپر سے رابطہ کریں۔"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"اپ ڈیٹ چیک کریں"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"آپ کے پاس نئے پیغامات ہیں"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"دیکھنے کیلئے SMS ایپ کھولیں"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"کچھ فعالیت محدود ہو سکتی ہے"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"آواز کیلئے فون کو اجازت نہیں ہے"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"پاپ اپ ونڈو"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"اس شارٹ کٹ کیلئے جدید ترین ایپ درکار ہے"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"شارٹ کٹ کو بحال نہیں کیا جا سکا، کیونکہ ایپ بیک اپ اور بحالی کو سپورٹ نہیں کرتی ہے"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ایپ دستخط غیر مماثل ہونے کی وجہ سے شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"شارٹ کٹ غیر فعال ہے"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"اَن انسٹال کریں"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"بہر صورت کھولیں"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"ضرر رساں ایپ کا پتہ چلا"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانا چاہتی ہے"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"ترمیم کریں"</string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 2329a41..c9076ca 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Xavfsiz usul"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android tizimi"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Shaxsiy profilga almashtirish"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Ishchi profilga almashtirish"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktlar"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktlarga kirish"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun kontaktlaringizga ruxsat berish"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ilovaga qurilmangizdagi telefon xususiyatlariga kirishga ruxsat beradi. Bu ruxsat ilovaga telefon raqami va qurilma nomlari, qo‘ng‘iroq faol yoki faolsizligi va masofadagi raqam qo‘ng‘rioq orqali bog‘langanligini aniqlashga imkon beradi."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"chaqiruvlarni tizim orqali yo‘naltirish"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ilova aloqa sifatini yaxshilash maqsadida chaqiruvlarni tizim orqali yo‘naltirishi mumkin."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"chaqiruvni boshqa ilovada davom ettirish"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ilovaga boshqa ilovada boshlangan chaqiruvni davom ettirish imkon beradi"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"telefon raqamlarini o‘qish"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Ilovaga qurilmaning telefon raqamlaridan foydalanishiga ruxsat beradi."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"planshetni uyquga ketishiga yo‘l qo‘ymaslik"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Barmoq izini saqlab bo‘lmadi. Mavjud barmoq izlaridan birini o‘chirib tashlang."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Barmoq izini aniqlash vaqti tugab qoldi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Barmoq izi tekshiruvi bekor qilindi."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Urinishlar soni ko‘payib ketdi. Keyinroq qayta urinib ko‘ring."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Urinishlar soni ko‘payib ketdi. Barmoq izi skaneri bloklandi."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Qayta urinib ko‘ring."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Ochish"</string>
<string name="sms" msgid="4560537514610063430">"Xabar"</string>
<string name="add_contact" msgid="7867066569670597203">"Qo‘shish"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Ochish"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Jadval"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Trek"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Xotirada bo‘sh joy tugamoqda"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ba‘zi tizim funksiyalari ishlamasligi mumkin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">Ochiq Wi-Fi tarmog‘i aniqlandi</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Ochiq Wi‑Fi tarmoqqa ulaning"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Aloqa operatori Wi-Fi tarmog‘iga ulanish"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Ochiq Wi‑Fi tarmoqqa ulanilmoqda"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Wi‑Fi tarmoqqa ulanildi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Wi-Fi tarmoqqa ulanib bo‘lmadi"</string>
@@ -1217,13 +1212,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Hech qanday ruxsat talab qilinmaydi"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"buning uchun sizdan haq olinishi mumkin"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB orqali quvvatlash"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB orqali ulangan qurilma quvvatlanmoqda"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB orqali fayl o‘tkazish"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB orqali surat o‘tkazish"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB orqali MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB jihozga ulangan"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Boshqa parametrlarini ko‘rish uchun bosing."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analogli audio uskuna aniqlandi"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Biriktirilgan qurilma mazkur telefon bilan mos emas. Batafsil axborot olish uchun bu yerga bosing."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB orqali nosozliklarni tuzatish"</string>
@@ -1662,9 +1667,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Ish <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2-ishxona <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3-ishxona <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Bu ekrandan chiqish uchun Orqaga va Menyu tugmalarini bosib turing"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Ekran qadab qo‘yildi"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran bo‘shatildi"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Yechishda PIN-kod so‘ralsin"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Yechishdan oldin grafik kalit so‘ralsin"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Bo‘shatishdan oldin parol so‘ralsin"</string>
@@ -1709,7 +1711,7 @@
</plurals>
<string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
<string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string>
- <string name="zen_mode_forever" msgid="931849471004038757">"O‘chirib qo‘ymagunimcha"</string>
+ <string name="zen_mode_forever" msgid="931849471004038757">"O‘chirmaguningizcha"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"“Bezovta qilinmasin” rejimi o‘chirilmaguncha"</string>
<string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="2821479483960330739">"Yig‘ish"</string>
@@ -1758,15 +1760,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Barcha tillar"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Barcha hududlar"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Qidiruv"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Ishchi profil yoqilsinmi?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Ishchi ilovalar, bildirishnomalar, ma’lumotlar va boshqa ishchi profil imkoniyatlari yoqiladi"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Yoqish"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Bu ilova eskiroq Android versiyalariga chiqarilgan va xato ishlashi mumkin. Yangilanishlarini tekshiring yoki dasturchi bilan bog‘laning."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Yangilanish borligini tekshirish"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Sizga yangi SMS keldi"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Ko‘rish uchun SMS ilovasini oching"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Ba’zi funksiyalar cheklanishi m-n"</string>
@@ -1833,17 +1831,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Ovoz uchun telefon taqiqlangan"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Qalqib chiquvchi oyna"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Bu yorliq uchun eng oxirgi versiyadagi ilova zarur"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ilovada zaxiralash va tiklash ishlamagani uchun yorliq tiklanmadi"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ilova imzosi mos kelmagani uchun yorliq tiklanmadi"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Yorliq tiklanmadi"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Yorliq faolsizlantirildi"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"O‘CHIRIB TASHLASH"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"BARIBIR OCHILSIN"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Zararli ilova aniqlandi"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasi <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatish uchun ruxsat so‘ramoqda"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Tahrirlash"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 2b4f6cb..b781f3e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>、<xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"切換至個人設定檔"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"切換至 Work 設定檔"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"聯絡人"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"存取你的聯絡人"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取你的聯絡人"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"允許應用程式使用裝置的電話功能。這項權限可讓應用程式判讀手機號碼和裝置 ID、是否正在通話中,以及所撥打的對方號碼。"</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"透過系統接通來電"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"允許應用程式透過系統接通來電,以改善通話品質。"</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"繼續進行來自其他應用程式的通話"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"允許應用程式繼續進行在其他應用程式中發起的通話。"</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"讀取電話號碼"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"允許應用程式存取裝置上的電話號碼資料。"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"防止平板電腦進入休眠狀態"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"無法儲存指紋,請先移除現有指紋。"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋處理作業逾時,請再試一次。"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"指紋作業已取消。"</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"使用者已取消指紋驗證作業。"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"嘗試次數過多,請稍後再試。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"嘗試次數過多,指紋感應器已停用。"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"請再試一次。"</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"開啟"</string>
<string name="sms" msgid="4560537514610063430">"訊息"</string>
<string name="add_contact" msgid="7867066569670597203">"新增"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"查看"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"時間表"</string>
+ <string name="view_flight" msgid="7691640491425680214">"追蹤"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1131,8 +1127,7 @@
<item quantity="one">有多個可用的開放 Wi-Fi 網路</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"連線至開放的 Wi‑Fi 網路"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"連上電信業者的 Wi‑Fi 網路"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"正在連線至開放的 Wi‑Fi 網路"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"已連線至 Wi-Fi 網路"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"無法連線至 Wi‑Fi 網路"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"無須許可"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"這可能需要付費"</string>
<string name="dlg_ok" msgid="7376953167039865701">"確定"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"正在透過 USB 為這個裝置充電"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"正在透過 USB 為連接的裝置供電"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB 檔案傳輸"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB 相片傳輸"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"已連接 USB 配件"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"輕觸即可查看更多選項。"</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"偵測到類比音訊配件"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"此外接裝置與這支手機不相容。輕觸即可瞭解詳情。"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"公司<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"第 2 項工作:<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"第 3 項工作:<xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"如要取消固定這個畫面,請按住「返回」按鈕和「總覽」按鈕"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"已固定螢幕"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"已取消固定螢幕"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"取消固定時必須輸入 PIN"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"取消固定時必須畫出解鎖圖案"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"取消固定時必須輸入密碼"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"所有語言"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"所有地區"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"搜尋"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"要開啟 Work 設定檔嗎?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"系統將開啟你的辦公應用程式、通知、資料和其他 Work 設定檔功能"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"開啟"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"這個應用程式是專為舊版 Android 所打造,因此可能無法正常運作。請嘗試檢查更新,或是與開發人員聯絡。"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"檢查更新"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"你有新訊息"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"開啟簡訊應用程式來查看內容"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"部分功能可能受到鎖定"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"手機不支援語音"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"彈出式視窗"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"你必須擁有最新版的應用程式,才能使用這個捷徑"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"應用程式不支援備份與還原功能,因此無法還原捷徑"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"應用程式簽署不相符,因此無法還原捷徑"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"無法還原捷徑"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"捷徑已停用"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"解除安裝"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"仍要開啟"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"偵測到有害應用程式"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"「<xliff:g id="APP_0">%1$s</xliff:g>」想要顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"編輯"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3f5d2b1..9c48610 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -264,10 +264,8 @@
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Imodi ephephile"</string>
<string name="android_system_label" msgid="6577375335728551336">"Uhlelo lwe-Android"</string>
- <!-- no translation found for user_owner_label (8836124313744349203) -->
- <skip />
- <!-- no translation found for managed_profile_label (8947929265267690522) -->
- <skip />
+ <string name="user_owner_label" msgid="8836124313744349203">"Shintshela kuphrofayela yomuntu siqu"</string>
+ <string name="managed_profile_label" msgid="8947929265267690522">"Shintshela kuphrofayela yomsebenzi"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Oxhumana nabo"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"finyelela koxhumana nabo"</string>
<string name="permgrouprequest_contacts" msgid="1601591667800538208">"Vumela i-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ukuthi ithole ukufinyelela koxhumana nabo"</string>
@@ -419,10 +417,8 @@
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Ivumela uhlelo lokusebenza ukufinyelela izici zefoni zedivayisi. Le mvume ivumela uhlelo lokusebenza ukucacisa inombolo yefoni nobunikazi bedivayisi, ukuthi noma ikholi iyasebenza, futhi nenombolo yesilawuli kude zixhunywe ngekholi."</string>
<string name="permlab_manageOwnCalls" msgid="1503034913274622244">"yanza imizila yamakholi ngesistimu"</string>
<string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Ivumela uhlelo lokusebenza ukwenza imizila yamakholi ngesistimu ukuze ithuthukise umuzwa wokushaya."</string>
- <!-- no translation found for permlab_acceptHandover (2661534649736022409) -->
- <skip />
- <!-- no translation found for permdesc_acceptHandovers (4570660484220539698) -->
- <skip />
+ <string name="permlab_acceptHandover" msgid="2661534649736022409">"qhuba ikholi kusukela kolunye uhlelo lokusebenza"</string>
+ <string name="permdesc_acceptHandovers" msgid="4570660484220539698">"Ivumela uhlelo lokusebenza ukuze luqhube ikholi eqalwe kolunye uhlelo lokusebenza."</string>
<string name="permlab_readPhoneNumbers" msgid="6108163940932852440">"funda izinombolo zefoni"</string>
<string name="permdesc_readPhoneNumbers" msgid="8559488833662272354">"Ivumela uhlelo lokusebenza ukufinyelela izinombolo zefoni zedivayisi."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"gwema ithebhulethi ukuba ingalali"</string>
@@ -497,11 +493,14 @@
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Izigxivizo zeminwe azikwazi ukugcinwa. Sicela ususe izigxivizo zeminwe ezikhona."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Ukusebenza kwezingxivizo zeminwe kukhanseliwe."</string>
- <!-- no translation found for fingerprint_error_user_canceled (7999639584615291494) -->
- <skip />
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Umsebenzi wesigxivizo somunwe sikhanselwe umsebenzisi."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Imizamo eminingi kakhulu. Inzwa yezigxivizo zeminwe ikhutshaziwe."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Zama futhi."</string>
+ <!-- no translation found for fingerprint_error_no_fingerprints (7654382120628334248) -->
+ <skip />
+ <!-- no translation found for fingerprint_error_hw_not_present (5729436878065119329) -->
+ <skip />
<string name="fingerprint_name_template" msgid="5870957565512716938">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1000,12 +999,9 @@
<string name="browse" msgid="1245903488306147205">"Vula"</string>
<string name="sms" msgid="4560537514610063430">"Umlayezo"</string>
<string name="add_contact" msgid="7867066569670597203">"Engeza"</string>
- <!-- no translation found for view_calendar (979609872939597838) -->
- <skip />
- <!-- no translation found for add_calendar_event (1953664627192056206) -->
- <skip />
- <!-- no translation found for view_flight (7691640491425680214) -->
- <skip />
+ <string name="view_calendar" msgid="979609872939597838">"Buka"</string>
+ <string name="add_calendar_event" msgid="1953664627192056206">"Ishejuli"</string>
+ <string name="view_flight" msgid="7691640491425680214">"Landelela"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -1131,8 +1127,7 @@
<item quantity="other">Vula amanethiwekhi we-Wi-Fi atholakalayo</item>
</plurals>
<string name="wifi_available_title" msgid="3817100557900599505">"Xhuma kunethiwekhi evulekile ye-Wi‑Fi"</string>
- <!-- no translation found for wifi_available_carrier_network_title (4527932626916527897) -->
- <skip />
+ <string name="wifi_available_carrier_network_title" msgid="4527932626916527897">"Xhumeka kunethiwekhi yenkampani yenethiwekhi ye-Wi-Fi"</string>
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Ixhuma kunethiwekhi evulekile ye-Wi‑Fi"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Kuxhumeke kunethiwekhi ye-Wi‑Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Ayikwazanga ukuxhumeka kunethiwekhi ye-Wi-Fi"</string>
@@ -1216,13 +1211,23 @@
<string name="no_permissions" msgid="7283357728219338112">"Ayikho imvume edingekayo"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"lokhu kungakudlela imali"</string>
<string name="dlg_ok" msgid="7376953167039865701">"KULUNGILE"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"I-USB ishaja le divayisi"</string>
- <string name="usb_supplying_notification_title" msgid="5310642257296510271">"I-USB inikeza amandla kudivayisi enamathiselwe"</string>
- <string name="usb_mtp_notification_title" msgid="8396264943589760855">"I-USB yokudluliswa kwefayela"</string>
- <string name="usb_ptp_notification_title" msgid="1347328437083192112">"I-USB yokudluliswa kwesithombe"</string>
- <string name="usb_midi_notification_title" msgid="4850904915889144654">"I-USB ye-MIDI"</string>
- <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ixhunywe ku-accessory ye-USB"</string>
+ <!-- no translation found for usb_charging_notification_title (1595122345358177163) -->
+ <skip />
+ <!-- no translation found for usb_supplying_notification_title (4631045789893086181) -->
+ <skip />
+ <!-- no translation found for usb_mtp_notification_title (4238227258391151029) -->
+ <skip />
+ <!-- no translation found for usb_ptp_notification_title (5425857879922006878) -->
+ <skip />
+ <!-- no translation found for usb_tether_notification_title (3716143122035802501) -->
+ <skip />
+ <!-- no translation found for usb_midi_notification_title (5356040379749154805) -->
+ <skip />
+ <!-- no translation found for usb_accessory_notification_title (1899977434994900306) -->
+ <skip />
<string name="usb_notification_message" msgid="3370903770828407960">"Thepha ngezinketho eziningi."</string>
+ <!-- no translation found for usb_power_notification_message (4647527153291917218) -->
+ <skip />
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Kutholwe isisetshenziswa se-analog yomsindo"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Idivayisi enamathiselwe kwi-imeyili ayihambisani nale foni. Thepha ukuze ufunde kabanzi."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ukulungisa iphutha le-USB kuxhunyiwe"</string>
@@ -1661,9 +1666,6 @@
<string name="managed_profile_label_badge" msgid="2355652472854327647">"Umsebenzi <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Umsebenzi wesibili <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Umsebenzi wesithathu <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="lock_to_app_toast" msgid="6820571533009838261">"Ukuze ususe ukuphina lesi sikrini, thinta uphinde ubambe izinkinobho zokubuyela emuva nezokubuka konke"</string>
- <string name="lock_to_app_start" msgid="6643342070839862795">"Isikrini siphiniwe"</string>
- <string name="lock_to_app_exit" msgid="8598219838213787430">"Isikrini sisuswe ukuphina"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Cela iphinikhodi ngaphambi kokuphina"</string>
<string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Cela iphethini yokuvula ngaphambi kokususa ukuphina"</string>
<string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Cela iphasiwedi ngaphambi kokususa ukuphina"</string>
@@ -1757,15 +1759,11 @@
<string name="language_picker_section_all" msgid="3097279199511617537">"Zonke izilimi"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"Zonke izifunda"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"Sesha"</string>
- <!-- no translation found for work_mode_off_title (1118691887588435530) -->
- <skip />
- <!-- no translation found for work_mode_off_message (5130856710614337649) -->
- <skip />
+ <string name="work_mode_off_title" msgid="1118691887588435530">"Vula iphrofayela yomsebenzi?"</string>
+ <string name="work_mode_off_message" msgid="5130856710614337649">"Izinhlelo zakho zokusebenza zomsebenzi, izaziso, idatha, nezinye izici zephrofayela yomsebenzi kuzovulwa"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Vula"</string>
- <!-- no translation found for deprecated_target_sdk_message (1449696506742572767) -->
- <skip />
- <!-- no translation found for deprecated_target_sdk_app_store (5032340500368495077) -->
- <skip />
+ <string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Lolu hlelo lokusebenza belakhelwe inguqulo endala ye-Android futhi kungenzeka lungasebenzi kahle. Zama ukuhlolela izibuyekezo, noma uxhumane nonjiniyela."</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Hlola izibuyekezo"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Unemilayezo emisha"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Vula uhlelo lokusebenza lwe-SMS ukuze ubuke"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Okunye ukusebenza kungakhawulelwe"</string>
@@ -1832,17 +1830,15 @@
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Ifoni ayivunyelwe izwi"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iwindi lesigelekeqe"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Lesi sinqamuleli sidinga uhlelo lokusebenza lwakamuva"</string>
+ <!-- no translation found for shortcut_restored_on_lower_version (4860853725206702336) -->
+ <skip />
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ayikwazanga ukubuyisa isinqamuleli ngoba uhlelo lokusebenza alusekeli isipele nokubuyisa"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ayikwazanga ukubuyisa isinqamuleli ngoba isignisha yohlelo lokusebenza ayifani"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Ayikwazanga ukubuyisa isinqamuleli"</string>
<string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Isinqamuleli sikhutshaziwe"</string>
- <!-- no translation found for harmful_app_warning_uninstall (4837672735619532931) -->
- <skip />
- <!-- no translation found for harmful_app_warning_open_anyway (596432803680914321) -->
- <skip />
- <!-- no translation found for harmful_app_warning_title (8982527462829423432) -->
- <skip />
+ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"KHIPHA"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"VULA NOMA KUNJALO"</string>
+ <string name="harmful_app_warning_title" msgid="8982527462829423432">"Uhlelo lokusebenza oluyingozi lutholakele"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"I-<xliff:g id="APP_0">%1$s</xliff:g> ifuna ukubonisa izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Hlela"</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 68dad87..005d07d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7903,6 +7903,23 @@
<attr name="settingsActivity" />
</declare-styleable>
+ <!-- Use <code>compatibility-package</code> as a child tag of <code>autofill-service</code>
+ in the XML resource that describes an {@link android.service.autofill.AutofillService}
+ to specify a package and an optional max version code for which to request compatibility
+ mode. If no max version code is specified compatibility mode is requested for all package
+ versions. The max version code is useful to avoid requesting compatibility mode for newer
+ package versions that are known to natively support autofill.
+ -->
+ <declare-styleable name="AutofillService_CompatibilityPackage">
+ <!-- The package name for which compatibility mode is requested. -->
+ <attr name="name" />
+ <!-- The max version code of the package for which compatibility mode is
+ requested. This corresponds to the long value returned by {@link
+ android.content.pm.PackageInfo#getLongVersionCode()} for the target package.
+ -->
+ <attr name="maxLongVersionCode" format="string" />
+ </declare-styleable>
+
<!-- =============================== -->
<!-- Contacts meta-data attributes -->
<!-- =============================== -->
@@ -8798,7 +8815,7 @@
<!-- Attributes that are read when parsing a tag. -->
<declare-styleable name="VideoView2">
<attr name="enableControlView" format="boolean" />
- <attr name="showSubtitle" format="boolean" />
+ <attr name="enableSubtitle" format="boolean" />
<attr name="viewType" format="enum">
<enum name="surfaceView" value="0" />
<enum name="textureView" value="1" />
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index ce4ac61..e80f16c 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -79,8 +79,8 @@
<item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
<item name="highlight_alpha_material_light" format="float" type="dimen">0.16</item>
- <item name="highlight_alpha_material_dark" format="float" type="dimen">0.32</item>
- <item name="highlight_alpha_material_colored" format="float" type="dimen">0.48</item>
+ <item name="highlight_alpha_material_dark" format="float" type="dimen">0.16</item>
+ <item name="highlight_alpha_material_colored" format="float" type="dimen">0.16</item>
<!-- Primary & accent colors -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 607414d..9dffe69 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -363,6 +363,23 @@
<!-- Regex of wired ethernet ifaces -->
<string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
+
+
+ <!-- Configuration of Ethernet interfaces in the following format:
+ <interface name|mac address>;[Network Capabilities];[IP config]
+ Where
+ [Network Capabilities] Optional. A comma seprated list of network capabilities.
+ Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
+ [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
+ static IP address with the mask.
+ -->
+ <string-array translatable="false" name="config_ethernet_interfaces">
+ <!--
+ <item>eth1;12,13,14,15;192.168.0.10/24</item>
+ <item>eth2;;192.168.0.11/24</item>
+ -->
+ </string-array>
+
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
@@ -432,12 +449,6 @@
<!-- Activity name to enable wifi tethering after provisioning app succeeds -->
<string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
- <!-- Controls the WiFi wakeup feature.
- 0 = Not available.
- 1 = Available.
- -->
- <integer translatable="false" name="config_wifi_wakeup_available">1</integer>
-
<!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering.
Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
@@ -1132,7 +1143,7 @@
<bool name="config_hotswapCapable">false</bool>
<!-- Component name of the ICC hotswap prompt for restart dialog -->
- <string name="config_iccHotswapPromptForRestartDialogComponent" translateable="false">@null</string>
+ <string name="config_iccHotswapPromptForRestartDialogComponent" translatable="false">@null</string>
<!-- Enable puk unlockscreen by default.
If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
@@ -1431,7 +1442,7 @@
permission.
[This is only used if config_enableUpdateableTimeZoneRules and
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
- <string name="config_timeZoneRulesUpdaterPackage" translateable="false">com.android.timezone.updater</string>
+ <string name="config_timeZoneRulesUpdaterPackage" translatable="false">com.android.timezone.updater</string>
<!-- The package of the time zone rules data application. Expected to be configured
by OEMs to reference their own priv-app APK package.
@@ -1440,7 +1451,7 @@
data app packages.
[This is only used if config_enableUpdateableTimeZoneRules and
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
- <string name="config_timeZoneRulesDataPackage" translateable="false"></string>
+ <string name="config_timeZoneRulesDataPackage" translatable="false"></string>
<!-- The allowed time in milliseconds between an update check intent being broadcast and the
response being considered overdue. Reliability triggers will not fire in this time.
@@ -2475,9 +2486,9 @@
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
<item>restart</item>
- <item>screenshot</item>
- <item>logout</item>
<item>lockdown</item>
+ <item>logout</item>
+ <item>screenshot</item>
<item>bugreport</item>
<item>users</item>
</string-array>
@@ -2573,6 +2584,14 @@
<!-- Flag specifying whether or not IMS will use the dynamic ImsResolver -->
<bool name="config_dynamic_bind_ims">false</bool>
+ <!-- Cellular data service package name to bind to by default. If none is specified in an overlay, an
+ empty string is passed in -->
+ <string name="config_wwan_data_service_package" translatable="false">com.android.phone</string>
+
+ <!-- IWLAN data service package name to bind to by default. If none is specified in an overlay, an
+ empty string is passed in -->
+ <string name="config_wlan_data_service_package" translatable="false"></string>
+
<bool name="config_networkSamplingWakesDevice">true</bool>
<!-- Home (non-roaming) values for CDMA roaming indicator.
@@ -3258,7 +3277,7 @@
<string translatable="false" name="config_deviceSpecificAudioService"></string>
<!-- Component name of media projection permission dialog -->
- <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
+ <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">2dp</dimen>
@@ -3277,4 +3296,11 @@
<bool name="config_supportBluetoothPersistedState">true</bool>
<bool name="config_keepRestrictedProfilesInBackground">true</bool>
+
+ <!-- Cellular network service package name to bind to by default. -->
+ <string name="config_wwan_network_service_package" translatable="false">com.android.phone</string>
+
+ <!-- IWLAN network service package name to bind to by default. If none is specified in an overlay, an
+ empty string is passed in -->
+ <string name="config_wlan_network_service_package" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e610efd..0411c6e 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -262,6 +262,18 @@
<!-- The spacing between messages in Notification.MessagingStyle -->
<dimen name="notification_messaging_spacing">6dp</dimen>
+ <!-- The rounding for messaging images -->
+ <dimen name="messaging_image_rounding">4dp</dimen>
+
+ <!-- The minimum size for any image in messaging style in order to be displayed -->
+ <dimen name="messaging_image_min_size">44dp</dimen>
+
+ <!-- The maximum size for any image in messaging style in order to be displayed -->
+ <dimen name="messaging_image_max_height">136dp</dimen>
+
+ <!-- Extra spacing before and after images in messaging style -->
+ <dimen name="messaging_image_extra_spacing">8dp</dimen>
+
<!-- Preferred width and height of the search view. -->
<dimen name="search_view_preferred_width">320dip</dimen>
<dimen name="search_view_preferred_height">48dip</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index dee880f..93f22f25 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2868,6 +2868,7 @@
<public name="accessibilityHeading" />
<public name="outlineSpotShadowColor" />
<public name="outlineAmbientShadowColor" />
+ <public name="maxLongVersionCode" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3b752c4..ec81df7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1351,6 +1351,10 @@
<string name="fingerprint_error_lockout_permanent">Too many attempts. Fingerprint sensor disabled.</string>
<!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
<string name="fingerprint_error_unable_to_process">Try again.</string>
+ <!-- Generic error message shown when the user has no enrolled fingerprints -->
+ <string name="fingerprint_error_no_fingerprints">No fingerprints enrolled.</string>
+ <!-- Generic error message shown when the app requests fingerprint authentication on a device without a sensor -->
+ <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor</string>
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
@@ -4806,7 +4810,7 @@
A toast message shown when an app shortcut that was restored from a previous device is clicked,
but it cannot be started because the shortcut was created by a newer version of the app.
-->
- <string name="shortcut_restored_on_lower_version">This shortcut requires latest app</string>
+ <string name="shortcut_restored_on_lower_version">App version downgraded, or isn\u2019t compatible with this shortcut</string>
<!--
A toast message shown when an app shortcut that was restored from a previous device is clicked,
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fee5a80..f7b6f06a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -266,6 +266,10 @@
<java-symbol type="bool" name="config_mms_content_disposition_support" />
<java-symbol type="string" name="config_ims_package" />
<java-symbol type="bool" name="config_dynamic_bind_ims" />
+ <java-symbol type="string" name="config_wwan_network_service_package" />
+ <java-symbol type="string" name="config_wlan_network_service_package" />
+ <java-symbol type="string" name="config_wwan_data_service_package" />
+ <java-symbol type="string" name="config_wlan_data_service_package" />
<java-symbol type="bool" name="config_networkSamplingWakesDevice" />
<java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
<java-symbol type="bool" name="config_sip_wifi_only" />
@@ -656,6 +660,7 @@
<java-symbol type="string" name="chooseActivity" />
<java-symbol type="string" name="config_default_dns_server" />
<java-symbol type="string" name="config_ethernet_iface_regex" />
+ <java-symbol type="array" name="config_ethernet_interfaces" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -2152,7 +2157,6 @@
<java-symbol type="string" name="config_mobile_hotspot_provision_response" />
<java-symbol type="integer" name="config_mobile_hotspot_provision_check_period" />
<java-symbol type="string" name="config_wifi_tether_enable" />
- <java-symbol type="integer" name="config_wifi_wakeup_available" />
<java-symbol type="bool" name="config_intrusiveNotificationLed" />
<java-symbol type="bool" name="config_notificationBadging" />
<java-symbol type="dimen" name="preference_fragment_padding_bottom" />
@@ -2351,6 +2355,8 @@
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
<java-symbol type="string" name="fingerprint_not_recognized" />
+ <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
+ <java-symbol type="string" name="fingerprint_error_hw_not_present" />
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
@@ -2699,9 +2705,6 @@
<java-symbol type="string" name="language_selection_title" />
<java-symbol type="string" name="search_language_hint" />
- <java-symbol type="layout" name="unlaunchable_app_activity" />
- <java-symbol type="id" name="unlaunchable_app_title" />
- <java-symbol type="id" name="unlaunchable_app_message" />
<java-symbol type="string" name="work_mode_off_title" />
<java-symbol type="string" name="work_mode_off_message" />
<java-symbol type="string" name="work_mode_turn_on" />
@@ -3150,6 +3153,7 @@
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
<java-symbol type="string" name="config_headlineFontFamily" />
<java-symbol type="string" name="config_headlineFontFamilyLight" />
+ <java-symbol type="string" name="config_headlineFontFamilyMedium" />
<java-symbol type="drawable" name="stat_sys_vitals" />
@@ -3164,7 +3168,8 @@
<java-symbol type="dimen" name="chooser_service_spacing" />
<java-symbol type="bool" name="config_showSysuiShutdown" />
- <java-symbol type="layout" name="notification_template_messaging_message" />
+ <java-symbol type="layout" name="notification_template_messaging_text_message" />
+ <java-symbol type="layout" name="notification_template_messaging_image_message" />
<java-symbol type="layout" name="notification_template_messaging_group" />
<java-symbol type="id" name="message_text" />
<java-symbol type="id" name="message_name" />
@@ -3179,6 +3184,11 @@
<java-symbol type="id" name="clip_children_tag" />
<java-symbol type="drawable" name="ic_reply_notification_large" />
<java-symbol type="dimen" name="messaging_avatar_size" />
+ <java-symbol type="dimen" name="messaging_image_rounding" />
+ <java-symbol type="dimen" name="messaging_image_min_size" />
+ <java-symbol type="dimen" name="messaging_image_max_height" />
+ <java-symbol type="dimen" name="messaging_image_extra_spacing" />
+ <java-symbol type="id" name="messaging_group_icon_container" />
<java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
<java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
diff --git a/core/res/res/layout/notification_template_messaging_message.xml b/core/res/res/xml/autofill_compat_accessibility_service.xml
similarity index 67%
copy from core/res/res/layout/notification_template_messaging_message.xml
copy to core/res/res/xml/autofill_compat_accessibility_service.xml
index ab6466c..327afbe 100644
--- a/core/res/res/layout/notification_template_messaging_message.xml
+++ b/core/res/res/xml/autofill_compat_accessibility_service.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
- ~ 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,8 +15,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.internal.widget.MessagingMessage
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/message_text"
- style="@style/Widget.Material.Notification.MessagingText"
-/>
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:accessibilityFlags="flagReportViewIds" />
diff --git a/core/res/res/xml/color_extraction.xml b/core/res/res/xml/color_extraction.xml
index 7d52b20..93ab0ff 100644
--- a/core/res/res/xml/color_extraction.xml
+++ b/core/res/res/xml/color_extraction.xml
@@ -257,52 +257,58 @@
<!-- Red Orange -->
<range h="20, 40"
s="0.7, 1"
- l="0.28, 0.643"/>
+ l="0.2, 0.643"/>
<range h="20, 40"
s="0.3, 0.7"
l="0.414, 0.561"/>
<range h="20, 40"
- s="0, 3"
+ s="0, 0.3"
l="0.343, 0.584"/>
<!-- Orange -->
<range h="40, 60"
s="0.7, 1"
- l="0.173, 0.349"/>
+ l="0.173, 0.38"/>
<range h="40, 60"
s="0.3, 0.7"
l="0.233, 0.427"/>
<range h="40, 60"
s="0, 0.3"
- l="0.231, 0.484"/>
+ l="0.231, 0.48"/>
<!-- Yellow 60 -->
<range h="60, 80"
s="0.7, 1"
- l="0.488, 0.737"/>
+ l="0.15, 0.40"/>
<range h="60, 80"
s="0.3, 0.7"
- l="0.673, 0.837"/>
+ l="0.15, 0.42"/>
+ <range h="60, 80"
+ s="0, 0.3"
+ l="0.35, 0.57"/>
<!-- Yellow Green 80 -->
<range h="80, 100"
s="0.7, 1"
- l="0.469, 0.61"/>
+ l="0.36, 0.65"/>
+ <range h="80, 100"
+ s="0.3, 0.7"
+ l="0.48, 0.57"/>
<!-- Yellow green 100 -->
<range h="100, 120"
s="0.7, 1"
- l="0.388, 0.612"/>
+ l="0.388, 0.67"/>
<range h="100, 120"
s="0.3, 0.7"
- l="0.424, 0.541"/>
+ l="0.424, 0.58"/>
<!-- Green -->
<range h="120, 140"
s="0.7, 1"
- l="0.375, 0.52"/>
+ l="0.37, 0.65"/>
<range h="120, 140"
s="0.3, 0.7"
- l="0.435, 0.524"/>
+ l="0.435, 0.58"/>
<!-- Green Blue 140 -->
<range h="140, 160"
s="0.7, 1"
- l="0.496, 0.641"/>
+ l="0.43, 0.641"/>
<!-- Seaoam -->
<range h="160, 180"
s="0.7, 1"
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
new file mode 100644
index 0000000..5989da7
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -0,0 +1,106 @@
+/*
+ * 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.os;
+
+import static android.os.FileUtils.copyInternalSendfile;
+import static android.os.FileUtils.copyInternalSplice;
+import static android.os.FileUtils.copyInternalUserspace;
+
+import android.os.FileUtils.MemoryPipe;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Param;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class FileUtilsBenchmark {
+ @Param({"32", "32000", "32000000"})
+ private int mSize;
+
+ private File mSrc;
+ private File mDest;
+
+ private byte[] mData;
+
+ @BeforeExperiment
+ protected void setUp() throws Exception {
+ mSrc = new File("/data/local/tmp/src");
+ mDest = new File("/data/local/tmp/dest");
+
+ mData = new byte[mSize];
+
+ try (FileOutputStream os = new FileOutputStream(mSrc)) {
+ os.write(mData);
+ }
+ }
+
+ public void timeRegularUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timeRegularSendfile(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSendfile(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSourceUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSourceSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSinkUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ public void timePipeSinkSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/res/layout/activity_text_view.xml b/core/tests/coretests/res/layout/activity_text_view.xml
index dca1656..d5be87d 100644
--- a/core/tests/coretests/res/layout/activity_text_view.xml
+++ b/core/tests/coretests/res/layout/activity_text_view.xml
@@ -24,6 +24,9 @@
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <Space
+ android:layout_width="1dp"
+ android:layout_height="60dp"/>
<TextView
android:id="@+id/nonselectable_textview"
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index aefc47e..fb0f534 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -17,6 +17,7 @@
package android.app.servertransaction;
import static android.app.servertransaction.TestUtils.config;
+import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
@@ -151,6 +152,25 @@
}
@Test
+ public void testRecycleActivityRelaunchItem() {
+ ActivityRelaunchItem emptyItem = ActivityRelaunchItem.obtain(null, null, 0, null, false);
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.assetsSeq = 5;
+ ActivityRelaunchItem item = ActivityRelaunchItem.obtain(resultInfoList(),
+ referrerIntentList(), 42, mergedConfig(), true);
+ assertNotSame(item, emptyItem);
+ assertFalse(item.equals(emptyItem));
+
+ item.recycle();
+ assertEquals(item, emptyItem);
+
+ ActivityRelaunchItem item2 = ActivityRelaunchItem.obtain(resultInfoList(),
+ referrerIntentList(), 42, mergedConfig(), true);
+ assertSame(item, item2);
+ assertFalse(item2.equals(emptyItem));
+ }
+
+ @Test
public void testRecycleMoveToDisplayItem() {
MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(0, null);
MoveToDisplayItem item = MoveToDisplayItem.obtain(4, config());
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index e923516..d125fe7 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -21,6 +21,7 @@
import android.app.ResultInfo;
import android.content.Intent;
import android.content.res.Configuration;
+import android.util.MergedConfiguration;
import com.android.internal.content.ReferrerIntent;
@@ -38,6 +39,15 @@
return config;
}
+ static MergedConfiguration mergedConfig() {
+ Configuration config = config();
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.densityDpi = 30;
+ overrideConfig.screenWidthDp = 40;
+ overrideConfig.smallestScreenWidthDp = 15;
+ return new MergedConfiguration(config, overrideConfig);
+ }
+
static List<ResultInfo> resultInfoList() {
String resultWho1 = "resultWho1";
int requestCode1 = 7;
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index e4de526..0906435 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -17,6 +17,7 @@
package android.app.servertransaction;
import static android.app.servertransaction.TestUtils.config;
+import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
@@ -27,7 +28,6 @@
import android.app.IInstrumentationWatcher;
import android.app.IUiAutomationConnection;
import android.app.ProfilerInfo;
-import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -53,7 +53,6 @@
import android.support.test.runner.AndroidJUnit4;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.content.ReferrerIntent;
import org.junit.Before;
import org.junit.Test;
@@ -243,6 +242,22 @@
}
@Test
+ public void testRelaunch() {
+ // Write to parcel
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.assetsSeq = 5;
+ ActivityRelaunchItem item = ActivityRelaunchItem.obtain(resultInfoList(),
+ referrerIntentList(), 35, mergedConfig(), true);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ ActivityRelaunchItem result = ActivityRelaunchItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
public void testPause() {
// Write to parcel
PauseActivityItem item = PauseActivityItem.obtain(true /* finished */,
@@ -389,8 +404,8 @@
Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher,
IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
boolean b2, boolean b3, Configuration configuration,
- CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1)
- throws RemoteException {
+ CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
+ boolean autofillCompatEnabled) throws RemoteException {
}
@Override
@@ -435,12 +450,6 @@
}
@Override
- public void scheduleRelaunchActivity(IBinder iBinder, List<ResultInfo> list,
- List<ReferrerIntent> list1, int i, boolean b, Configuration configuration,
- Configuration configuration1, boolean b1) throws RemoteException {
- }
-
- @Override
public void scheduleSleeping(IBinder iBinder, boolean b) throws RemoteException {
}
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
new file mode 100644
index 0000000..f90ae34
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.hardware.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AmbientBrightnessDayStatsTest {
+
+ private static final LocalDate LOCAL_DATE = LocalDate.now();
+ private static final float[] BUCKET_BOUNDARIES = {0, 1, 10, 100};
+ private static final float[] STATS = {1.3f, 2.6f, 5.8f, 10};
+
+ @Test
+ public void testParamsMustNotBeNull() {
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES, STATS));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null, STATS));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBucketBoundariesMustNotBeEmpty() {
+ new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testStatsAndBoundariesMustHaveSameLength() {
+ float[] stats = Arrays.copyOf(STATS, STATS.length + 1);
+ stats[stats.length - 1] = 0;
+ new AmbientBrightnessDayStats(LOCAL_DATE, BUCKET_BOUNDARIES, stats);
+ }
+
+ @Test
+ public void testAmbientBrightnessDayStatsAdd() {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
+ dayStats.log(0, 1);
+ dayStats.log(0.5f, 1.5f);
+ dayStats.log(50, 12.5f);
+ dayStats.log(2000, 1.24f);
+ dayStats.log(-10, 0.5f);
+ assertEquals(4, dayStats.getStats().length);
+ assertEquals(2.5f, dayStats.getStats()[0], 0);
+ assertEquals(0, dayStats.getStats()[1], 0);
+ assertEquals(12.5f, dayStats.getStats()[2], 0);
+ assertEquals(1.24f, dayStats.getStats()[3], 0);
+ }
+
+ @Test
+ public void testGetters() {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(LOCAL_DATE, dayStats.getLocalDate());
+ assertArrayEquals(BUCKET_BOUNDARIES, dayStats.getBucketBoundaries(), 0);
+ assertArrayEquals(STATS, dayStats.getStats(), 0);
+ }
+
+ @Test
+ public void testParcelUnparcelAmbientBrightnessDayStats() {
+ LocalDate today = LocalDate.now();
+ AmbientBrightnessDayStats stats = new AmbientBrightnessDayStats(today,
+ new float[]{0, 1, 10, 100}, new float[]{1.3f, 2.6f, 5.8f, 10});
+ // Parcel the data
+ Parcel parcel = Parcel.obtain();
+ stats.writeToParcel(parcel, 0);
+ byte[] parceled = parcel.marshall();
+ parcel.recycle();
+ // Unparcel and check that it has not changed
+ parcel = Parcel.obtain();
+ parcel.unmarshall(parceled, 0, parceled.length);
+ parcel.setDataPosition(0);
+ AmbientBrightnessDayStats statsAgain = AmbientBrightnessDayStats.CREATOR.createFromParcel(
+ parcel);
+ assertEquals(stats, statsAgain);
+ }
+
+ @Test
+ public void testAmbientBrightnessDayStatsEquals() {
+ AmbientBrightnessDayStats emptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
+ AmbientBrightnessDayStats identicalEmptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, new float[BUCKET_BOUNDARIES.length]);
+ assertEquals(emptyDayStats, identicalEmptyDayStats);
+ assertEquals(emptyDayStats.hashCode(), identicalEmptyDayStats.hashCode());
+
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ AmbientBrightnessDayStats identicalDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(dayStats, identicalDayStats);
+ assertEquals(dayStats.hashCode(), identicalDayStats.hashCode());
+
+ assertNotEquals(emptyDayStats, dayStats);
+ assertNotEquals(emptyDayStats.hashCode(), dayStats.hashCode());
+
+ AmbientBrightnessDayStats differentDateDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE.plusDays(1), BUCKET_BOUNDARIES, STATS);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentDateDayStats.hashCode());
+
+ float[] differentStats = Arrays.copyOf(STATS, STATS.length);
+ differentStats[differentStats.length - 1] += 5f;
+ AmbientBrightnessDayStats differentStatsDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, differentStats);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentStatsDayStats.hashCode());
+
+ float[] differentBucketBoundaries = Arrays.copyOf(BUCKET_BOUNDARIES,
+ BUCKET_BOUNDARIES.length);
+ differentBucketBoundaries[differentBucketBoundaries.length - 1] += 100f;
+ AmbientBrightnessDayStats differentBoundariesDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE, differentBucketBoundaries, STATS);
+ assertNotEquals(dayStats, differentBoundariesDayStats);
+ assertNotEquals(dayStats.hashCode(), differentBoundariesDayStats.hashCode());
+ }
+
+ private interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+
+ private static void assertThrows(Class<? extends Throwable> exceptionClass,
+ ExceptionRunnable r) {
+ try {
+ r.run();
+ } catch (Throwable e) {
+ assertTrue("Expected exception type " + exceptionClass.getName() + " but got "
+ + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass()));
+ return;
+ }
+ fail("Expected exception type " + exceptionClass.getName()
+ + ", but no exception was thrown");
+ }
+
+}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 27b7f9e..ea0347d 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -192,6 +192,12 @@
assertEquals("a:a@example.com:a@example2.com", uri.getAuthority());
assertEquals("example2.com", uri.getHost());
assertEquals(-1, uri.getPort());
+ assertEquals("/path", uri.getPath());
+
+ uri = Uri.parse("http://a.foo.com\\.example.com/path");
+ assertEquals("a.foo.com", uri.getHost());
+ assertEquals(-1, uri.getPort());
+ assertEquals("\\.example.com/path", uri.getPath());
}
@SmallTest
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index cd20192..0bc3a2d 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,16 +21,19 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.FileUtils.MemoryPipe;
import android.provider.DocumentsContract.Document;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import libcore.io.IoUtils;
+import libcore.io.Streams;
import com.google.android.collect.Sets;
@@ -40,11 +43,13 @@
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Random;
@RunWith(AndroidJUnit4.class)
public class FileUtilsTest {
@@ -56,6 +61,8 @@
private File mCopyFile;
private File mTarget;
+ private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };
+
private Context getContext() {
return InstrumentationRegistry.getContext();
}
@@ -80,7 +87,7 @@
@Test
public void testCopyFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertFalse(mCopyFile.exists());
FileUtils.copyFile(mTestFile, mCopyFile);
assertTrue(mCopyFile.exists());
@@ -97,6 +104,104 @@
}
@Test
+ public void testCopy_FileToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in, out);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_FileToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_ShortPipeToFile() throws Exception {
+ byte[] source = new byte[33_000_000];
+ new Random().nextBytes(source);
+
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = Arrays.copyOf(source, size);
+ byte[] actual = new byte[size];
+
+ try (MemoryPipe in = MemoryPipe.createSource(source);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD(), null, null, size);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
public void testIsFilenameSafe() throws Exception {
assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -106,7 +211,7 @@
@Test
public void testReadTextFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
@@ -127,7 +232,7 @@
@Test
public void testReadTextFileWithZeroLengthFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
new FileOutputStream(mTestFile).close(); // Zero out the file
assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
@@ -381,12 +486,21 @@
file.setLastModified(System.currentTimeMillis() - age);
}
- private void stageFile(File file, String data) throws Exception {
- FileWriter writer = new FileWriter(file);
- try {
- writer.write(data, 0, data.length());
- } finally {
- writer.close();
+ private void writeFile(File file, String data) throws Exception {
+ writeFile(file, data.getBytes());
+ }
+
+ private void writeFile(File file, byte[] data) throws Exception {
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ out.write(data);
+ }
+ }
+
+ private byte[] readFile(File file) throws Exception {
+ try (FileInputStream in = new FileInputStream(file);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ Streams.copy(in, out);
+ return out.toByteArray();
}
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0083b01..853a36d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -109,12 +109,15 @@
Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
Settings.Global.ANIMATOR_DURATION_SCALE,
Settings.Global.ANOMALY_DETECTION_CONSTANTS,
+ Settings.Global.ANOMALY_CONFIG,
+ Settings.Global.ANOMALY_CONFIG_VERSION,
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
Settings.Global.APN_DB_UPDATE_METADATA_URL,
Settings.Global.APP_IDLE_CONSTANTS,
Settings.Global.APP_STANDBY_ENABLED,
Settings.Global.ASSISTED_GPS_ENABLED,
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
+ Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -193,6 +196,7 @@
Settings.Global.DEVICE_PROVISIONED,
Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
+ Settings.Global.DISPLAY_PANEL_LPM,
Settings.Global.DISPLAY_SCALING_FORCE,
Settings.Global.DISPLAY_SIZE_FORCED,
Settings.Global.DNS_RESOLVER_MAX_SAMPLES,
@@ -436,11 +440,10 @@
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED,
Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED,
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
Settings.Global.WIFI_WATCHDOG_ON,
Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON,
Settings.Global.WINDOW_ANIMATION_SCALE,
- Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
+ Settings.Global.CHARGING_STARTED_SOUND,
Settings.Global.WTF_IS_FATAL,
Settings.Global.ZEN_MODE,
Settings.Global.ZEN_MODE_CONFIG_ETAG,
diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS
index 0f85e1f..9f2182e 100644
--- a/core/tests/coretests/src/android/text/OWNERS
+++ b/core/tests/coretests/src/android/text/OWNERS
@@ -1,4 +1,3 @@
siyamed@google.com
nona@google.com
clarabayarri@google.com
-toki@google.com
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
index 59d4e55..657a7fc 100644
--- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
@@ -41,6 +41,13 @@
testMenuPosition(getActivity().getTargetRtl());
}
+ public void testContextMenuPositionRepetitive() throws InterruptedException {
+ // Regression test for b/72507876
+ testMenuPosition(getActivity().getTargetLtr());
+ testMenuPosition(getActivity().getTargetRtl());
+ testMenuPosition(getActivity().getTargetLtr());
+ }
+
private void testMenuPosition(View target) throws InterruptedException {
final int minScreenDimension = getMinScreenDimension();
if (minScreenDimension < 320) {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 69e5670..7f4f9f7 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -66,6 +66,7 @@
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
import android.test.suitebuilder.annotation.Suppress;
import android.text.InputType;
import android.text.Selection;
@@ -311,15 +312,69 @@
@Test
public void testToolbarAppearsAfterLinkClicked() throws Throwable {
- runToolbarAppearsAfterLinkClickedTest(R.id.textview);
+ TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
+ int position = (textLink.getStart() + textLink.getEnd()) / 2;
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
}
@Test
public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable {
- runToolbarAppearsAfterLinkClickedTest(R.id.nonselectable_textview);
+ TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
+ int position = (textLink.getStart() + textLink.getEnd()) / 2;
+ onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
}
- private void runToolbarAppearsAfterLinkClickedTest(int id) throws Throwable {
+ @Test
+ public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable {
+ // Add a link to both selectable and nonselectable TextViews:
+ TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
+ int selectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
+ textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
+ int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
+ TextView selectableTextView = mActivity.findViewById(R.id.textview);
+ TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview);
+
+ onView(withId(R.id.nonselectable_textview))
+ .perform(clickOnTextAtIndex(nonselectablePosition));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ assertTrue(nonselectableTextView.hasSelection());
+
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(selectablePosition));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
+ assertTrue(selectableTextView.hasSelection());
+ assertFalse(nonselectableTextView.hasSelection());
+ }
+
+ @Test
+ public void testSelectionRemovedFromNonselectableTextWhenWindowLosesFocus() throws Throwable {
+ TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
+ int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
+ TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview);
+
+ onView(withId(R.id.nonselectable_textview))
+ .perform(clickOnTextAtIndex(nonselectablePosition));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ assertTrue(nonselectableTextView.hasSelection());
+
+ UiDevice device = UiDevice.getInstance(mInstrumentation);
+ device.openNotification();
+ Thread.sleep(2000);
+ device.pressBack();
+ Thread.sleep(2000);
+
+ assertFalse(nonselectableTextView.hasSelection());
+ }
+
+ private TextLinks.TextLink addLinkifiedTextToTextView(int id) throws Throwable {
TextView textView = mActivity.findViewById(id);
useSystemDefaultTextClassifier();
TextClassificationManager textClassificationManager =
@@ -338,11 +393,7 @@
// Wait for the UI thread to refresh
Thread.sleep(1000);
- TextLinks.TextLink textLink = links.getLinks().iterator().next();
- int position = (textLink.getStart() + textLink.getEnd()) / 2;
- onView(withId(id)).perform(clickOnTextAtIndex(position));
- sleepForFloatingToolbarPopup();
- assertFloatingToolbarIsDisplayed();
+ return links.getLinks().iterator().next();
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 660c744..7b239f0 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -20,6 +20,7 @@
import android.os.Looper;
import android.util.SparseIntArray;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import java.util.ArrayList;
import java.util.concurrent.Future;
@@ -40,6 +41,11 @@
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
setExternalStatsSyncLocked(new DummyExternalStatsSync());
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000-i, null,
+ mOnBatteryTimeBase);
+ }
+
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {};
}
diff --git a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
index 3919fdd..41082b7 100644
--- a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
@@ -127,7 +127,7 @@
assertEquals(355, mView.getMeasuredHeight());;
}
- private class FakeImageFloatingTextView extends MessagingMessage {
+ private class FakeImageFloatingTextView extends MessagingTextMessage {
public static final int LINE_HEIGHT = 50;
private final int mNumLines;
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
new file mode 100644
index 0000000..4ca3e4c
--- /dev/null
+++ b/core/tests/overlaytests/device/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_TARGET_REQUIRED_MODULES := \
+ OverlayDeviceTests_AppOverlayOne \
+ OverlayDeviceTests_AppOverlayTwo \
+ OverlayDeviceTests_FrameworkOverlay
+include $(BUILD_PACKAGE)
+
+# Include to build test-apps.
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index e01caee..d14fdf5 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -15,15 +15,15 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.om.test">
+ package="com.android.overlaytest">
<uses-sdk android:minSdkVersion="21" />
<application>
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.om.test" />
-
+ android:targetPackage="com.android.overlaytest"
+ android:label="Runtime resource overlay tests" />
</manifest>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
new file mode 100644
index 0000000..f069835
--- /dev/null
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<configuration description="Test module config for OverlayDeviceTests">
+ <option name="test-tag" value="OverlayDeviceTests" />
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="OverlayDeviceTests.apk" />
+ <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
+ <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
+ <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="cmd overlay disable com.android.overlaytest.framework" />
+ <option name="run-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_one" />
+ <option name="run-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_two" />
+ <option name="teardown-command"
+ value="cmd overlay disable com.android.overlaytest.framework" />
+ <option name="teardown-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_one" />
+ <option name="teardown-command"
+ value="cmd overlay disable com.android.overlaytest.app_overlay_two" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.overlaytest" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk
deleted file mode 100644
index f76de7a..0000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := system_current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml
deleted file mode 100644
index 5b7950a..0000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.filtered_app_overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.overlaytest"
- android:requiredSystemPropertyName="persist.oem.overlay.test"
- android:requiredSystemPropertyValue="foo"
- android:priority="3"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt
deleted file mode 100644
index 0954ced..0000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt
+++ /dev/null
@@ -1 +0,0 @@
-Lorem ipsum: filtered overlays.
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml b/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml
deleted file mode 100644
index 60b94ee..0000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="str">filtered</string>
-</resources>
diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml b/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml
deleted file mode 100644
index e2652b7..0000000
--- a/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<integer value="3"/>
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/Android.mk b/core/tests/overlaytests/device/OverlayAppFirst/Android.mk
deleted file mode 100644
index bf9416c..0000000
--- a/core/tests/overlaytests/device/OverlayAppFirst/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.first_app_overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml
deleted file mode 100644
index ec10bbc..0000000
--- a/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.first_app_overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.overlaytest" android:priority="1"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/Android.mk b/core/tests/overlaytests/device/OverlayAppSecond/Android.mk
deleted file mode 100644
index bb7d142..0000000
--- a/core/tests/overlaytests/device/OverlayAppSecond/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.second_app_overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml
deleted file mode 100644
index ed49863..0000000
--- a/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.second_app_overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.overlaytest" android:priority="2"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTest/Android.mk b/core/tests/overlaytests/device/OverlayTest/Android.mk
deleted file mode 100644
index 5fe7b91..0000000
--- a/core/tests/overlaytests/device/OverlayTest/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PACKAGE_NAME := OverlayTest
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVA_LIBRARIES += android.test.base
-
-LOCAL_MODULE_PATH := $(TARGET_OUT)/app
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml
deleted file mode 100644
index 9edba12..0000000
--- a/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest">
- <uses-permission android:name="android.permission.RUN_INSTRUMENTATION"/>
- <application>
- <uses-library android:name="android.test.runner"/>
- </application>
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.overlaytest"
- android:label="Runtime resource overlay tests"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java
deleted file mode 100644
index e104f5a..0000000
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.overlaytest;
-
-public class WithMultipleOverlaysTest extends OverlayBaseTest {
- public WithMultipleOverlaysTest() {
- mMode = MODE_MULTIPLE_OVERLAYS;
- }
-}
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
deleted file mode 100644
index 816a476..0000000
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.overlaytest;
-
-public class WithOverlayTest extends OverlayBaseTest {
- public WithOverlayTest() {
- mMode = MODE_SINGLE_OVERLAY;
- }
-}
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
deleted file mode 100644
index 318cccc..0000000
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.overlaytest;
-
-public class WithoutOverlayTest extends OverlayBaseTest {
- public WithoutOverlayTest() {
- mMode = MODE_NO_OVERLAY;
- }
-}
diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk
deleted file mode 100644
index ed33046..0000000
--- a/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml
deleted file mode 100644
index f8b6c7b..0000000
--- a/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.overlaytest.overlay"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="android" android:priority="1"/>
-</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg
similarity index 100%
rename from core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg
rename to core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg
Binary files differ
diff --git a/core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/res/raw/lorem_ipsum.txt
similarity index 100%
rename from core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt
rename to core/tests/overlaytests/device/res/raw/lorem_ipsum.txt
diff --git a/core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml b/core/tests/overlaytests/device/res/values-sv/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml
rename to core/tests/overlaytests/device/res/values-sv/config.xml
diff --git a/core/tests/overlaytests/device/OverlayTest/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayTest/res/values/config.xml
rename to core/tests/overlaytests/device/res/values/config.xml
diff --git a/core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml b/core/tests/overlaytests/device/res/xml/integer.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml
rename to core/tests/overlaytests/device/res/xml/integer.xml
diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
similarity index 76%
rename from core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
rename to core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index e57c55c..96ab977 100644
--- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -1,45 +1,80 @@
+/*
+ * 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.overlaytest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.UiAutomation;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.test.AndroidTestCase;
+import android.os.LocaleList;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
import android.util.AttributeSet;
import android.util.Xml;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.Locale;
-public abstract class OverlayBaseTest extends AndroidTestCase {
+@Ignore
+public abstract class OverlayBaseTest {
private Resources mResources;
- protected int mMode; // will be set by subclasses
- static final protected int MODE_NO_OVERLAY = 0;
- static final protected int MODE_SINGLE_OVERLAY = 1;
- static final protected int MODE_MULTIPLE_OVERLAYS = 2;
+ private final int mMode;
+ static final int MODE_NO_OVERLAY = 0;
+ static final int MODE_SINGLE_OVERLAY = 1;
+ static final int MODE_MULTIPLE_OVERLAYS = 2;
- protected void setUp() {
- mResources = getContext().getResources();
+ static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
+ static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
+ static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework";
+
+ protected OverlayBaseTest(int mode) {
+ mMode = mode;
+ }
+
+ @Before
+ public void setUp() {
+ mResources = InstrumentationRegistry.getContext().getResources();
}
private int calculateRawResourceChecksum(int resId) throws Throwable {
- InputStream input = null;
- try {
- input = mResources.openRawResource(resId);
+ try (InputStream input = mResources.openRawResource(resId)) {
int ch, checksum = 0;
while ((ch = input.read()) != -1) {
checksum = (checksum + ch) % 0xffddbb00;
}
return checksum;
- } finally {
- input.close();
}
}
private void setLocale(Locale locale) {
- Locale.setDefault(locale);
+ final LocaleList locales = new LocaleList(locale);
+ LocaleList.setDefault(locales);
Configuration config = new Configuration();
- config.locale = locale;
+ config.setLocales(locales);
mResources.updateConfiguration(config, mResources.getDisplayMetrics());
}
@@ -126,6 +161,7 @@
}
}
+ @Test
public void testFrameworkBooleanOverlay() throws Throwable {
// config_annoy_dianne has the value:
// - true when no overlay exists (MODE_NO_OVERLAY)
@@ -135,6 +171,7 @@
assertResource(resId, true, false, false);
}
+ @Test
public void testBooleanOverlay() throws Throwable {
// usually_false has the value:
// - false when no overlay exists (MODE_NO_OVERLAY)
@@ -144,12 +181,14 @@
assertResource(resId, false, true, false);
}
+ @Test
public void testBoolean() throws Throwable {
// always_true has no overlay
final int resId = R.bool.always_true;
assertResource(resId, true, true, true);
}
+ @Test
public void testIntegerArrayOverlay() throws Throwable {
// fibonacci has values:
// - eight first values of Fibonacci sequence, when no overlay exists (MODE_NO_OVERLAY)
@@ -162,6 +201,7 @@
new int[]{21, 13, 8, 5, 3, 2, 1, 1});
}
+ @Test
public void testIntegerArray() throws Throwable {
// prime_numbers has no overlay
final int resId = R.array.prime_numbers;
@@ -169,6 +209,7 @@
assertResource(resId, expected, expected, expected);
}
+ @Test
public void testDrawable() throws Throwable {
// drawable-nodpi/drawable has overlay (default config)
final int resId = R.drawable.drawable;
@@ -188,16 +229,19 @@
assertEquals(expected, actual);
}
+ @Test
public void testAppString() throws Throwable {
final int resId = R.string.str;
assertResource(resId, "none", "single", "multiple");
}
+ @Test
public void testApp2() throws Throwable {
final int resId = R.string.str2; // only in base package and first app overlay
assertResource(resId, "none", "single", "single");
}
+ @Test
public void testAppXml() throws Throwable {
int expected = getExpected(0, 1, 2);
int actual = -1;
@@ -214,6 +258,7 @@
assertEquals(expected, actual);
}
+ @Test
public void testAppRaw() throws Throwable {
final int resId = R.raw.lorem_ipsum;
@@ -256,10 +301,10 @@
* SLOT PACKAGE CONFIGURATION VALUE
* A target package (default) 100
* B target package -sv 200
- * C OverlayAppFirst (default) 300
- * D OverlayAppFirst -sv 400
- * E OverlayAppSecond (default) 500
- * F OverlayAppSecond -sv 600
+ * C AppOverlayOne (default) 300
+ * D AppOverlayOne -sv 400
+ * E AppOverlayTwo (default) 500
+ * F AppOverlayTwo -sv 600
*
* Example: in testMatrix101110, the base package defines the
* R.integer.matrix101110 resource for the default configuration (value
@@ -269,195 +314,283 @@
* are loaded, the expected value after setting the language to Swedish is
* 400.
*/
+ @Test
public void testMatrix100000() throws Throwable {
final int resId = R.integer.matrix_100000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 100);
}
+ @Test
public void testMatrix100001() throws Throwable {
final int resId = R.integer.matrix_100001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
+ @Test
public void testMatrix100010() throws Throwable {
final int resId = R.integer.matrix_100010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 500);
}
+ @Test
public void testMatrix100011() throws Throwable {
final int resId = R.integer.matrix_100011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 100, 600);
}
+ @Test
public void testMatrix100100() throws Throwable {
final int resId = R.integer.matrix_100100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix100101() throws Throwable {
final int resId = R.integer.matrix_100101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix100110() throws Throwable {
final int resId = R.integer.matrix_100110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix100111() throws Throwable {
final int resId = R.integer.matrix_100111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix101000() throws Throwable {
final int resId = R.integer.matrix_101000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 300);
}
+ @Test
public void testMatrix101001() throws Throwable {
final int resId = R.integer.matrix_101001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
+ @Test
public void testMatrix101010() throws Throwable {
final int resId = R.integer.matrix_101010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 500);
}
+ @Test
public void testMatrix101011() throws Throwable {
final int resId = R.integer.matrix_101011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 300, 600);
}
+ @Test
public void testMatrix101100() throws Throwable {
final int resId = R.integer.matrix_101100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix101101() throws Throwable {
final int resId = R.integer.matrix_101101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix101110() throws Throwable {
final int resId = R.integer.matrix_101110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 400);
}
+ @Test
public void testMatrix101111() throws Throwable {
final int resId = R.integer.matrix_101111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 100, 400, 600);
}
+ @Test
public void testMatrix110000() throws Throwable {
final int resId = R.integer.matrix_110000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix110001() throws Throwable {
final int resId = R.integer.matrix_110001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix110010() throws Throwable {
final int resId = R.integer.matrix_110010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix110011() throws Throwable {
final int resId = R.integer.matrix_110011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix110100() throws Throwable {
final int resId = R.integer.matrix_110100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix110101() throws Throwable {
final int resId = R.integer.matrix_110101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+ @Test
public void testMatrix110110() throws Throwable {
final int resId = R.integer.matrix_110110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix110111() throws Throwable {
final int resId = R.integer.matrix_110111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+ @Test
public void testMatrix111000() throws Throwable {
final int resId = R.integer.matrix_111000;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix111001() throws Throwable {
final int resId = R.integer.matrix_111001;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix111010() throws Throwable {
final int resId = R.integer.matrix_111010;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 200);
}
+ @Test
public void testMatrix111011() throws Throwable {
final int resId = R.integer.matrix_111011;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 200, 600);
}
+ @Test
public void testMatrix111100() throws Throwable {
final int resId = R.integer.matrix_111100;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix111101() throws Throwable {
final int resId = R.integer.matrix_111101;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+ @Test
public void testMatrix111110() throws Throwable {
final int resId = R.integer.matrix_111110;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 400);
}
+ @Test
public void testMatrix111111() throws Throwable {
final int resId = R.integer.matrix_111111;
setLocale(new Locale("sv", "SE"));
assertResource(resId, 200, 400, 600);
}
+
+ /**
+ * Executes the shell command and reads all the output to ensure the command ran and didn't
+ * get stuck buffering on output.
+ */
+ protected static String executeShellCommand(UiAutomation automation, String command)
+ throws Exception {
+ final ParcelFileDescriptor pfd = automation.executeShellCommand(command);
+ try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ final BufferedReader reader = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8));
+ StringBuilder str = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ str.append(line);
+ }
+ return str.toString();
+ }
+ }
+
+ /**
+ * Enables overlay packages and waits for a configuration change event before
+ * returning, to guarantee that Resources are up-to-date.
+ * @param packages the list of package names to enable.
+ */
+ protected static void enableOverlayPackages(String... packages) throws Exception {
+ enableOverlayPackages(true, packages);
+ }
+
+ /**
+ * Disables overlay packages and waits for a configuration change event before
+ * returning, to guarantee that Resources are up-to-date.
+ * @param packages the list of package names to disable.
+ */
+ protected static void disableOverlayPackages(String... packages) throws Exception {
+ enableOverlayPackages(false, packages);
+ }
+
+ /**
+ * Enables/disables overlay packages and waits for a configuration change event before
+ * returning, to guarantee that Resources are up-to-date.
+ * @param enable enables the overlays when true, disables when false.
+ * @param packages the list of package names to enable/disable.
+ */
+ private static void enableOverlayPackages(boolean enable, String[] packages)
+ throws Exception {
+ final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+ for (final String pkg : packages) {
+ executeShellCommand(uiAutomation,
+ "cmd overlay " + (enable ? "enable " : "disable ") + pkg);
+ }
+
+ // Wait for the overlay change to propagate.
+ Thread.sleep(1000);
+ }
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
new file mode 100644
index 0000000..f35e511
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -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.
+ */
+
+package com.android.overlaytest;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class WithMultipleOverlaysTest extends OverlayBaseTest {
+ public WithMultipleOverlaysTest() {
+ super(MODE_MULTIPLE_OVERLAYS);
+ }
+
+ @BeforeClass
+ public static void enableOverlay() throws Exception {
+ enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
new file mode 100644
index 0000000..037449f
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class WithOverlayTest extends OverlayBaseTest {
+ public WithOverlayTest() {
+ super(MODE_SINGLE_OVERLAY);
+ }
+
+ @BeforeClass
+ public static void enableOverlay() throws Exception {
+ disableOverlayPackages(APP_OVERLAY_TWO_PKG);
+ enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG);
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
new file mode 100644
index 0000000..f657b5c
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -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.
+ */
+
+package com.android.overlaytest;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class WithoutOverlayTest extends OverlayBaseTest {
+ public WithoutOverlayTest() {
+ super(MODE_NO_OVERLAY);
+ }
+
+ @BeforeClass
+ public static void disableOverlays() throws Exception {
+ disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG);
+ }
+}
diff --git a/core/tests/overlaytests/device/test-apps/Android.mk b/core/tests/overlaytests/device/test-apps/Android.mk
new file mode 100644
index 0000000..9af9f444
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# 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 $(call all-subdir-makefiles)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
new file mode 100644
index 0000000..17e20ee
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk
@@ -0,0 +1,22 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayOne
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
new file mode 100644
index 0000000..1719158
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.app_overlay_one"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest" android:priority="1" />
+</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg
rename to core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg
Binary files differ
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt
rename to core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml
rename to core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml
rename to core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml
rename to core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
new file mode 100644
index 0000000..c24bea9
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk
@@ -0,0 +1,22 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayTwo
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
new file mode 100644
index 0000000..ae8307c
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.app_overlay_two"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest" android:priority="2" />
+</manifest>
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt
rename to core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml
rename to core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml
rename to core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml
diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml
rename to core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml
diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
new file mode 100644
index 0000000..dc811c5
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk
@@ -0,0 +1,22 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayDeviceTests_FrameworkOverlay
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..77ea16a
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.framework"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:priority="1" />
+</manifest>
diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml
similarity index 100%
rename from core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml
rename to core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
index d8e1fc1..b48a46b 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/core/tests/overlaytests/host/Android.mk
@@ -24,7 +24,11 @@
OverlayHostTests_BadSignatureOverlay \
OverlayHostTests_PlatformSignatureStaticOverlay \
OverlayHostTests_PlatformSignatureOverlay \
- OverlayHostTests_PlatformSignatureOverlayV2
+ OverlayHostTests_UpdateOverlay \
+ OverlayHostTests_FrameworkOverlayV1 \
+ OverlayHostTests_FrameworkOverlayV2 \
+ OverlayHostTests_AppOverlayV1 \
+ OverlayHostTests_AppOverlayV2
include $(BUILD_HOST_JAVA_LIBRARY)
# Include to build test-apps.
diff --git a/core/tests/overlaytests/host/AndroidTest.xml b/core/tests/overlaytests/host/AndroidTest.xml
index 6884623..1f750a8 100644
--- a/core/tests/overlaytests/host/AndroidTest.xml
+++ b/core/tests/overlaytests/host/AndroidTest.xml
@@ -18,6 +18,11 @@
<option name="test-tag" value="OverlayHostTests" />
<option name="test-suite-tag" value="apct" />
+ <!-- Install the device tests that will be used to verify things on device. -->
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="OverlayHostTests_UpdateOverlay.apk" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.HostTest">
<option name="class" value="com.android.server.om.hosttest.InstallOverlayTests" />
</test>
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 5093710..bf91a16 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -23,14 +23,48 @@
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class InstallOverlayTests extends BaseHostJUnit4Test {
-
- private static final String OVERLAY_PACKAGE_NAME =
+ private static final String SIG_OVERLAY_PACKAGE_NAME =
"com.android.server.om.hosttest.signature_overlay";
+ private static final String APP_OVERLAY_PACKAGE_NAME =
+ "com.android.server.om.hosttest.app_overlay";
+ private static final String FRAMEWORK_OVERLAY_PACKAGE_NAME =
+ "com.android.server.om.hosttest.framework_overlay";
+ private static final String[] ALL_PACKAGES = new String[] {
+ SIG_OVERLAY_PACKAGE_NAME, APP_OVERLAY_PACKAGE_NAME, FRAMEWORK_OVERLAY_PACKAGE_NAME
+ };
+
+ private static final String DEVICE_TEST_PKG =
+ "com.android.server.om.hosttest.update_overlay_test";
+ private static final String DEVICE_TEST_CLS = DEVICE_TEST_PKG + ".UpdateOverlayTest";
+
+ @Before
+ public void ensureNoOverlays() throws Exception {
+ // Make sure we're starting with a clean slate.
+ for (String pkg : ALL_PACKAGES) {
+ assertFalse(pkg + " should not be installed", isPackageInstalled(pkg));
+ assertFalse(pkg + " should not be registered with overlay manager service",
+ overlayManagerContainsPackage(pkg));
+ }
+ }
+
+ /*
+ For some reason, SuiteApkInstaller is *not* uninstalling overlays, even though #installPackage()
+ claims it will auto-clean.
+ TODO(b/72877546): Remove when auto-clean is fixed.
+ */
+ @After
+ public void uninstallOverlays() throws Exception {
+ for (String pkg : ALL_PACKAGES) {
+ uninstallPackage(pkg);
+ }
+ }
@Test
public void failToInstallNonPlatformSignedOverlay() throws Exception {
@@ -40,7 +74,7 @@
} catch (Exception e) {
// Expected.
}
- assertFalse(overlayManagerContainsPackage());
+ assertFalse(overlayManagerContainsPackage(SIG_OVERLAY_PACKAGE_NAME));
}
@Test
@@ -51,28 +85,64 @@
} catch (Exception e) {
// Expected.
}
- assertFalse(overlayManagerContainsPackage());
+ assertFalse(overlayManagerContainsPackage(SIG_OVERLAY_PACKAGE_NAME));
}
@Test
- public void succeedToInstallPlatformSignedOverlay() throws Exception {
+ public void installPlatformSignedOverlay() throws Exception {
installPackage("OverlayHostTests_PlatformSignatureOverlay.apk");
- assertTrue(overlayManagerContainsPackage());
+ assertTrue(overlayManagerContainsPackage(SIG_OVERLAY_PACKAGE_NAME));
}
@Test
- public void succeedToInstallPlatformSignedOverlayAndUpdate() throws Exception {
- installPackage("OverlayHostTests_PlatformSignatureOverlay.apk");
- assertTrue(overlayManagerContainsPackage());
- assertEquals("v1", getDevice().getAppPackageInfo(OVERLAY_PACKAGE_NAME).getVersionName());
+ public void installPlatformSignedAppOverlayAndUpdate() throws Exception {
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectAppResource"));
- installPackage("OverlayHostTests_PlatformSignatureOverlayV2.apk");
- assertTrue(overlayManagerContainsPackage());
- assertEquals("v2", getDevice().getAppPackageInfo(OVERLAY_PACKAGE_NAME).getVersionName());
+ installPackage("OverlayHostTests_AppOverlayV1.apk");
+ setOverlayEnabled(APP_OVERLAY_PACKAGE_NAME, true);
+ assertTrue(overlayManagerContainsPackage(APP_OVERLAY_PACKAGE_NAME));
+ assertEquals("v1", getDevice()
+ .getAppPackageInfo(APP_OVERLAY_PACKAGE_NAME)
+ .getVersionName());
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+ "expectAppOverlayV1Resource"));
+
+ installPackage("OverlayHostTests_AppOverlayV2.apk");
+ assertTrue(overlayManagerContainsPackage(APP_OVERLAY_PACKAGE_NAME));
+ assertEquals("v2", getDevice()
+ .getAppPackageInfo(APP_OVERLAY_PACKAGE_NAME)
+ .getVersionName());
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+ "expectAppOverlayV2Resource"));
}
- private boolean overlayManagerContainsPackage() throws Exception {
- return getDevice().executeShellCommand("cmd overlay list")
- .contains(OVERLAY_PACKAGE_NAME);
+ @Test
+ public void installPlatformSignedFrameworkOverlayAndUpdate() throws Exception {
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectAppResource"));
+
+ installPackage("OverlayHostTests_FrameworkOverlayV1.apk");
+ setOverlayEnabled(FRAMEWORK_OVERLAY_PACKAGE_NAME, true);
+ assertTrue(overlayManagerContainsPackage(FRAMEWORK_OVERLAY_PACKAGE_NAME));
+ assertEquals("v1", getDevice()
+ .getAppPackageInfo(FRAMEWORK_OVERLAY_PACKAGE_NAME)
+ .getVersionName());
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+ "expectFrameworkOverlayV1Resource"));
+
+ installPackage("OverlayHostTests_FrameworkOverlayV2.apk");
+ assertTrue(overlayManagerContainsPackage(FRAMEWORK_OVERLAY_PACKAGE_NAME));
+ assertEquals("v2", getDevice()
+ .getAppPackageInfo(FRAMEWORK_OVERLAY_PACKAGE_NAME)
+ .getVersionName());
+ assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+ "expectFrameworkOverlayV2Resource"));
+ }
+
+ private void setOverlayEnabled(String pkg, boolean enabled) throws Exception {
+ getDevice().executeShellCommand("cmd overlay " + (enabled ? "enable " : "disable ") + pkg);
+ }
+
+ private boolean overlayManagerContainsPackage(String pkg) throws Exception {
+ return getDevice().executeShellCommand("cmd overlay list").contains(pkg);
}
}
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
index b051a82..4249549 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
@@ -40,13 +40,4 @@
LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
include $(BUILD_PACKAGE)
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlayV2
-LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-include $(BUILD_PACKAGE)
-
my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
index 2d68439..b08ac96 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml
@@ -15,6 +15,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.om.hosttest.signature_overlay">
+ package="com.android.server.om.hosttest.signature_overlay">
<overlay android:targetPackage="android" />
</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
new file mode 100644
index 0000000..bd6d73d
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -0,0 +1,73 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+include $(BUILD_PACKAGE)
+
+my_package_prefix := com.android.server.om.hosttest.framework_overlay
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
+LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v1/res
+LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
+LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v2/res
+LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+my_package_prefix := com.android.server.om.hosttest.app_overlay
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
+LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v1/res
+LOCAL_MANIFEST_FILE := app/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
+LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v2/res
+LOCAL_MANIFEST_FILE := app/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..06077a7
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.om.hosttest.update_overlay_test">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
+ android:label="Update Overlay Test"/>
+</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/AndroidManifest.xml
new file mode 100644
index 0000000..73804eb
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.om.hosttest.app_overlay">
+ <overlay android:targetPackage="com.android.server.om.hosttest.update_overlay_test" />
+</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v1/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v1/res/values/values.xml
new file mode 100644
index 0000000..63f85c2
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v1/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="app_resource">App Resource Overlay V1</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/res/values/values.xml
new file mode 100644
index 0000000..fa4a697
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="app_resource">App Resource Overlay V2</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/AndroidManifest.xml
new file mode 100644
index 0000000..8c8fe94
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.om.hosttest.framework_overlay">
+ <overlay android:targetPackage="android" />
+</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v1/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v1/res/values/values.xml
new file mode 100644
index 0000000..fedb2c6
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v1/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="ok">Framework Overlay V1</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v2/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v2/res/values/values.xml
new file mode 100644
index 0000000..8aebf483
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v2/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="ok">Framework Overlay V2</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/res/values/values.xml
new file mode 100644
index 0000000..7393166
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="app_resource">App Resource</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
new file mode 100644
index 0000000..d46bb37
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.om.hosttest.update_overlay_test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
+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;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UpdateOverlayTest {
+ private Resources mResources;
+
+ @Before
+ public void setUp() throws Exception {
+ final Configuration defaultLocaleConfiguration = new Configuration();
+ defaultLocaleConfiguration.setLocale(Locale.US);
+ mResources = InstrumentationRegistry
+ .getInstrumentation()
+ .getContext()
+ .createConfigurationContext(defaultLocaleConfiguration)
+ .getResources();
+ }
+
+ @Test
+ public void expectAppResource() throws Exception {
+ assertEquals("App Resource", mResources.getString(R.string.app_resource));
+ }
+
+ @Test
+ public void expectAppOverlayV1Resource() throws Exception {
+ assertEquals("App Resource Overlay V1", mResources.getString(R.string.app_resource));
+ }
+
+ @Test
+ public void expectAppOverlayV2Resource() throws Exception {
+ assertEquals("App Resource Overlay V2", mResources.getString(R.string.app_resource));
+ }
+
+ @Test
+ public void expectFrameworkOverlayResource() throws Exception {
+ assertEquals("OK", mResources.getString(android.R.string.ok));
+ }
+
+ @Test
+ public void expectFrameworkOverlayV1Resource() throws Exception {
+ assertEquals("Framework Overlay V1", mResources.getString(android.R.string.ok));
+ }
+
+ @Test
+ public void expectFrameworkOverlayV2Resource() throws Exception {
+ assertEquals("Framework Overlay V2", mResources.getString(android.R.string.ok));
+ }
+}
diff --git a/core/tests/overlaytests/testrunner.py b/core/tests/overlaytests/testrunner.py
deleted file mode 100755
index e88805e..0000000
--- a/core/tests/overlaytests/testrunner.py
+++ /dev/null
@@ -1,732 +0,0 @@
-#!/usr/bin/python
-import hashlib
-import optparse
-import os
-import re
-import shlex
-import subprocess
-import sys
-import threading
-import time
-
-TASK_COMPILATION = 'compile'
-TASK_DISABLE_OVERLAYS = 'disable overlays'
-TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays'
-TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay'
-TASK_ENABLE_FILTERED_OVERLAYS = 'enable filtered overlays'
-TASK_FILE_EXISTS_TEST = 'test (file exists)'
-TASK_GREP_IDMAP_TEST = 'test (grep idmap)'
-TASK_MD5_TEST = 'test (md5)'
-TASK_IDMAP_PATH = 'idmap --path'
-TASK_IDMAP_SCAN = 'idmap --scan'
-TASK_INSTRUMENTATION = 'instrumentation'
-TASK_INSTRUMENTATION_TEST = 'test (instrumentation)'
-TASK_MKDIR = 'mkdir'
-TASK_PUSH = 'push'
-TASK_ROOT = 'root'
-TASK_REMOUNT = 'remount'
-TASK_RM = 'rm'
-TASK_SETPROP = 'setprop'
-TASK_SETUP_IDMAP_PATH = 'setup idmap --path'
-TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan'
-TASK_START = 'start'
-TASK_STOP = 'stop'
-
-adb = 'adb'
-
-def _adb_shell(cmd):
- argv = shlex.split(adb + " shell '" + cmd + "; echo $?'")
- proc = subprocess.Popen(argv, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- (stdout, stderr) = (stdout.replace('\r', ''), stderr.replace('\r', ''))
- tmp = stdout.rsplit('\n', 2)
- if len(tmp) == 2:
- stdout == ''
- returncode = int(tmp[0])
- else:
- stdout = tmp[0] + '\n'
- returncode = int(tmp[1])
- return returncode, stdout, stderr
-
-class VerbosePrinter:
- class Ticker(threading.Thread):
- def _print(self):
- s = '\r' + self.text + '[' + '.' * self.i + ' ' * (4 - self.i) + ']'
- sys.stdout.write(s)
- sys.stdout.flush()
- self.i = (self.i + 1) % 5
-
- def __init__(self, cond_var, text):
- threading.Thread.__init__(self)
- self.text = text
- self.setDaemon(True)
- self.cond_var = cond_var
- self.running = False
- self.i = 0
- self._print()
- self.running = True
-
- def run(self):
- self.cond_var.acquire()
- while True:
- self.cond_var.wait(0.25)
- running = self.running
- if not running:
- break
- self._print()
- self.cond_var.release()
-
- def stop(self):
- self.cond_var.acquire()
- self.running = False
- self.cond_var.notify_all()
- self.cond_var.release()
-
- def _start_ticker(self):
- self.ticker = VerbosePrinter.Ticker(self.cond_var, self.text)
- self.ticker.start()
-
- def _stop_ticker(self):
- self.ticker.stop()
- self.ticker.join()
- self.ticker = None
-
- def _format_begin(self, type, name):
- N = self.width - len(type) - len(' [ ] ')
- fmt = '%%s %%-%ds ' % N
- return fmt % (type, name)
-
- def __init__(self, use_color):
- self.cond_var = threading.Condition()
- self.ticker = None
- if use_color:
- self.color_RED = '\033[1;31m'
- self.color_red = '\033[0;31m'
- self.color_reset = '\033[0;37m'
- else:
- self.color_RED = ''
- self.color_red = ''
- self.color_reset = ''
-
- argv = shlex.split('stty size') # get terminal width
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- if proc.returncode == 0:
- (h, w) = stdout.split()
- self.width = int(w)
- else:
- self.width = 72 # conservative guesstimate
-
- def begin(self, type, name):
- self.text = self._format_begin(type, name)
- sys.stdout.write(self.text + '[ ]')
- sys.stdout.flush()
- self._start_ticker()
-
- def end_pass(self, type, name):
- self._stop_ticker()
- sys.stdout.write('\r' + self.text + '[ OK ]\n')
- sys.stdout.flush()
-
- def end_fail(self, type, name, msg):
- self._stop_ticker()
- sys.stdout.write('\r' + self.color_RED + self.text + '[FAIL]\n')
- sys.stdout.write(self.color_red)
- sys.stdout.write(msg)
- sys.stdout.write(self.color_reset)
- sys.stdout.flush()
-
-class QuietPrinter:
- def begin(self, type, name):
- pass
-
- def end_pass(self, type, name):
- sys.stdout.write('PASS ' + type + ' ' + name + '\n')
- sys.stdout.flush()
-
- def end_fail(self, type, name, msg):
- sys.stdout.write('FAIL ' + type + ' ' + name + '\n')
- sys.stdout.flush()
-
-class CompilationTask:
- def __init__(self, makefile):
- self.makefile = makefile
-
- def get_type(self):
- return TASK_COMPILATION
-
- def get_name(self):
- return self.makefile
-
- def execute(self):
- os.putenv('ONE_SHOT_MAKEFILE', os.getcwd() + "/" + self.makefile)
- argv = shlex.split('make -C "../../../../../" files')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- return proc.returncode, stdout, stderr
-
-class InstrumentationTask:
- def __init__(self, instrumentation_class):
- self.instrumentation_class = instrumentation_class
-
- def get_type(self):
- return TASK_INSTRUMENTATION
-
- def get_name(self):
- return self.instrumentation_class
-
- def execute(self):
- return _adb_shell('am instrument -r -w -e class %s com.android.overlaytest/android.test.InstrumentationTestRunner' % self.instrumentation_class)
-
-class PushTask:
- def __init__(self, src, dest):
- self.src = src
- self.dest = dest
-
- def get_type(self):
- return TASK_PUSH
-
- def get_name(self):
- return "%s -> %s" % (self.src, self.dest)
-
- def execute(self):
- src = os.getenv('OUT')
- if (src is None):
- return 1, "", "Unable to proceed - $OUT environment var not set\n"
- src += "/" + self.src
- argv = shlex.split(adb + ' push %s %s' % (src, self.dest))
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- return proc.returncode, stdout, stderr
-
-class MkdirTask:
- def __init__(self, path):
- self.path = path
-
- def get_type(self):
- return TASK_MKDIR
-
- def get_name(self):
- return self.path
-
- def execute(self):
- return _adb_shell('mkdir -p %s' % self.path)
-
-class RmTask:
- def __init__(self, path):
- self.path = path
-
- def get_type(self):
- return TASK_RM
-
- def get_name(self):
- return self.path
-
- def execute(self):
- returncode, stdout, stderr = _adb_shell('ls %s' % self.path)
- if returncode != 0 and stderr.endswith(': No such file or directory\n'):
- return 0, "", ""
- return _adb_shell('rm -r %s' % self.path)
-
-class SetPropTask:
- def __init__(self, prop, value):
- self.prop = prop
- self.value = value
-
- def get_type(self):
- return TASK_SETPROP
-
- def get_name(self):
- return self.prop
-
- def execute(self):
- return _adb_shell('setprop %s %s' % (self.prop, self.value))
-
-class IdmapPathTask:
- def __init__(self, path_target_apk, path_overlay_apk, path_idmap):
- self.path_target_apk = path_target_apk
- self.path_overlay_apk = path_overlay_apk
- self.path_idmap = path_idmap
-
- def get_type(self):
- return TASK_IDMAP_PATH
-
- def get_name(self):
- return self.path_idmap
-
- def execute(self):
- return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.target_pkg_name, self.target_pkg, self.idmap_dir, self.overlay_dir))
-
-class IdmapScanTask:
- def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir):
- self.overlay_dir = overlay_dir
- self.target_pkg_name = target_pkg_name
- self.target_pkg = target_pkg
- self.idmap_dir = idmap_dir
- self.symlink_dir = symlink_dir
-
- def get_type(self):
- return TASK_IDMAP_SCAN
-
- def get_name(self):
- return self.target_pkg_name
-
- def execute(self):
- return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.overlay_dir, self.target_pkg_name, self.target_pkg, self.idmap_dir))
-
-class FileExistsTest:
- def __init__(self, path):
- self.path = path
-
- def get_type(self):
- return TASK_FILE_EXISTS_TEST
-
- def get_name(self):
- return self.path
-
- def execute(self):
- return _adb_shell('ls %s' % self.path)
-
-class GrepIdmapTest:
- def __init__(self, path_idmap, pattern, expected_n):
- self.path_idmap = path_idmap
- self.pattern = pattern
- self.expected_n = expected_n
-
- def get_type(self):
- return TASK_GREP_IDMAP_TEST
-
- def get_name(self):
- return self.pattern
-
- def execute(self):
- returncode, stdout, stderr = _adb_shell('idmap --inspect %s' % self.path_idmap)
- if returncode != 0:
- return returncode, stdout, stderr
- all_matches = re.findall('\s' + self.pattern + '$', stdout, flags=re.MULTILINE)
- if len(all_matches) != self.expected_n:
- return 1, 'pattern=%s idmap=%s expected=%d found=%d\n' % (self.pattern, self.path_idmap, self.expected_n, len(all_matches)), ''
- return 0, "", ""
-
-class Md5Test:
- def __init__(self, path, expected_content):
- self.path = path
- self.expected_md5 = hashlib.md5(expected_content).hexdigest()
-
- def get_type(self):
- return TASK_MD5_TEST
-
- def get_name(self):
- return self.path
-
- def execute(self):
- returncode, stdout, stderr = _adb_shell('md5sum %s' % self.path)
- if returncode != 0:
- return returncode, stdout, stderr
- actual_md5 = stdout.split()[0]
- if actual_md5 != self.expected_md5:
- return 1, 'expected %s, got %s\n' % (self.expected_md5, actual_md5), ''
- return 0, "", ""
-
-class StartTask:
- def get_type(self):
- return TASK_START
-
- def get_name(self):
- return ""
-
- def execute(self):
- (returncode, stdout, stderr) = _adb_shell('start')
- if returncode != 0:
- return returncode, stdout, stderr
-
- while True:
- (returncode, stdout, stderr) = _adb_shell('getprop dev.bootcomplete')
- if returncode != 0:
- return returncode, stdout, stderr
- if stdout.strip() == "1":
- break
- time.sleep(0.5)
-
- return 0, "", ""
-
-class StopTask:
- def get_type(self):
- return TASK_STOP
-
- def get_name(self):
- return ""
-
- def execute(self):
- (returncode, stdout, stderr) = _adb_shell('stop')
- if returncode != 0:
- return returncode, stdout, stderr
- return _adb_shell('setprop dev.bootcomplete 0')
-
-class RootTask:
- def get_type(self):
- return TASK_ROOT
-
- def get_name(self):
- return ""
-
- def execute(self):
- (returncode, stdout, stderr) = _adb_shell('getprop service.adb.root 0')
- if returncode != 0:
- return returncode, stdout, stderr
- if stdout.strip() == '1': # already root
- return 0, "", ""
-
- argv = shlex.split(adb + ' root')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- if proc.returncode != 0:
- return proc.returncode, stdout, stderr
-
- argv = shlex.split(adb + ' wait-for-device')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- return proc.returncode, stdout, stderr
-
-class RemountTask:
- def get_type(self):
- return TASK_REMOUNT
-
- def get_name(self):
- return ""
-
- def execute(self):
- argv = shlex.split(adb + ' remount')
- proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- # adb remount returns 0 even if the operation failed, so check stdout
- if stdout.startswith('remount failed:'):
- return 1, stdout, stderr
- return proc.returncode, stdout, stderr
-
-class CompoundTask:
- def __init__(self, type, tasks):
- self.type = type
- self.tasks = tasks
-
- def get_type(self):
- return self.type
-
- def get_name(self):
- return ""
-
- def execute(self):
- for t in self.tasks:
- (returncode, stdout, stderr) = t.execute()
- if returncode != 0:
- return returncode, stdout, stderr
- return 0, "", ""
-
-def _create_disable_overlays_task():
- tasks = [
- RmTask("/vendor/overlay/framework_a.apk"),
- RmTask("/vendor/overlay/framework_b.apk"),
- RmTask("/data/resource-cache/vendor@overlay@framework_a.apk@idmap"),
- RmTask("/data/resource-cache/vendor@overlay@framework_b.apk@idmap"),
- RmTask("/vendor/overlay/app_a.apk"),
- RmTask("/vendor/overlay/app_b.apk"),
- RmTask("/vendor/overlay/app_c.apk"),
- RmTask("/data/resource-cache/vendor@overlay@app_a.apk@idmap"),
- RmTask("/data/resource-cache/vendor@overlay@app_b.apk@idmap"),
- RmTask("/data/resource-cache/vendor@overlay@app_c.apk@idmap"),
- SetPropTask('persist.oem.overlay.test', '""'),
- RmTask("/data/property/persist.oem.overlay.test"),
- ]
- return CompoundTask(TASK_DISABLE_OVERLAYS, tasks)
-
-def _create_enable_single_overlay_task():
- tasks = [
- _create_disable_overlays_task(),
- MkdirTask('/system/vendor'),
- MkdirTask('/vendor/overlay'),
- PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_a.apk'),
- PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
- ]
- return CompoundTask(TASK_ENABLE_SINGLE_OVERLAY, tasks)
-
-def _create_enable_multiple_overlays_task():
- tasks = [
- _create_disable_overlays_task(),
- MkdirTask('/system/vendor'),
- MkdirTask('/vendor/overlay'),
-
- PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
- PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
- PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
- PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
- ]
- return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks)
-
-def _create_enable_filtered_overlays_task():
- tasks = [
- _create_disable_overlays_task(),
- SetPropTask('persist.oem.overlay.test', 'foo'),
- MkdirTask('/system/vendor'),
- MkdirTask('/vendor/overlay'),
- PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
- PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
- PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
- PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
- ]
- return CompoundTask(TASK_ENABLE_FILTERED_OVERLAYS, tasks)
-
-def _create_setup_idmap_path_task(idmaps, symlinks):
- tasks = [
- _create_enable_single_overlay_task(),
- RmTask(symlinks),
- RmTask(idmaps),
- MkdirTask(idmaps),
- MkdirTask(symlinks),
- ]
- return CompoundTask(TASK_SETUP_IDMAP_PATH, tasks)
-
-def _create_setup_idmap_scan_task(idmaps, symlinks):
- tasks = [
- _create_enable_filtered_overlays_task(),
- RmTask(symlinks),
- RmTask(idmaps),
- MkdirTask(idmaps),
- MkdirTask(symlinks),
- ]
- return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks)
-
-def _handle_instrumentation_task_output(stdout, printer):
- regex_status_code = re.compile(r'^INSTRUMENTATION_STATUS_CODE: -?(\d+)')
- regex_name = re.compile(r'^INSTRUMENTATION_STATUS: test=(.*)')
- regex_begin_stack = re.compile(r'^INSTRUMENTATION_STATUS: stack=(.*)')
- regex_end_stack = re.compile(r'^$')
-
- failed_tests = 0
- current_test = None
- current_stack = []
- mode_stack = False
- for line in stdout.split("\n"):
- line = line.rstrip() # strip \r from adb output
- m = regex_status_code.match(line)
- if m:
- c = int(m.group(1))
- if c == 1:
- printer.begin(TASK_INSTRUMENTATION_TEST, current_test)
- elif c == 0:
- printer.end_pass(TASK_INSTRUMENTATION_TEST, current_test)
- else:
- failed_tests += 1
- current_stack.append("\n")
- msg = "\n".join(current_stack)
- printer.end_fail(TASK_INSTRUMENTATION_TEST, current_test, msg.rstrip() + '\n')
- continue
-
- m = regex_name.match(line)
- if m:
- current_test = m.group(1)
- continue
-
- m = regex_begin_stack.match(line)
- if m:
- mode_stack = True
- current_stack = []
- current_stack.append(" " + m.group(1))
- continue
-
- m = regex_end_stack.match(line)
- if m:
- mode_stack = False
- continue
-
- if mode_stack:
- current_stack.append(" " + line.strip())
-
- return failed_tests
-
-def _set_adb_device(option, opt, value, parser):
- global adb
- if opt == '-d' or opt == '--device':
- adb = 'adb -d'
- if opt == '-e' or opt == '--emulator':
- adb = 'adb -e'
- if opt == '-s' or opt == '--serial':
- adb = 'adb -s ' + value
-
-def _create_opt_parser():
- parser = optparse.OptionParser()
- parser.add_option('-d', '--device', action='callback', callback=_set_adb_device,
- help='pass -d to adb')
- parser.add_option('-e', '--emulator', action='callback', callback=_set_adb_device,
- help='pass -e to adb')
- parser.add_option('-s', '--serial', type="str", action='callback', callback=_set_adb_device,
- help='pass -s <serical> to adb')
- parser.add_option('-C', '--no-color', action='store_false',
- dest='use_color', default=True,
- help='disable color escape sequences in output')
- parser.add_option('-q', '--quiet', action='store_true',
- dest='quiet_mode', default=False,
- help='quiet mode, output only results')
- parser.add_option('-b', '--no-build', action='store_false',
- dest='do_build', default=True,
- help='do not rebuild test projects')
- parser.add_option('-k', '--continue', action='store_true',
- dest='do_continue', default=False,
- help='do not rebuild test projects')
- parser.add_option('-i', '--test-idmap', action='store_true',
- dest='test_idmap', default=False,
- help='run tests for idmap')
- parser.add_option('-0', '--test-no-overlay', action='store_true',
- dest='test_no_overlay', default=False,
- help='run tests without any overlay')
- parser.add_option('-1', '--test-single-overlay', action='store_true',
- dest='test_single_overlay', default=False,
- help='run tests for single overlay')
- parser.add_option('-2', '--test-multiple-overlays', action='store_true',
- dest='test_multiple_overlays', default=False,
- help='run tests for multiple overlays')
- parser.add_option('-3', '--test-filtered-overlays', action='store_true',
- dest='test_filtered_overlays', default=False,
- help='run tests for filtered (sys prop) overlays')
- return parser
-
-if __name__ == '__main__':
- opt_parser = _create_opt_parser()
- opts, args = opt_parser.parse_args(sys.argv[1:])
- if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays and not opts.test_filtered_overlays:
- opts.test_idmap = True
- opts.test_no_overlay = True
- opts.test_single_overlay = True
- opts.test_multiple_overlays = True
- opts.test_filtered_overlays = True
-
- if len(args) > 0:
- opt_parser.error("unexpected arguments: %s" % " ".join(args))
- # will never reach this: opt_parser.error will call sys.exit
-
- if opts.quiet_mode:
- printer = QuietPrinter()
- else:
- printer = VerbosePrinter(opts.use_color)
- tasks = []
-
- # must be in the same directory as this script for compilation tasks to work
- script = sys.argv[0]
- dirname = os.path.dirname(script)
- wd = os.path.realpath(dirname)
- os.chdir(wd)
-
- # build test cases
- if opts.do_build:
- tasks.append(CompilationTask('OverlayTest/Android.mk'))
- tasks.append(CompilationTask('OverlayTestOverlay/Android.mk'))
- tasks.append(CompilationTask('OverlayAppFirst/Android.mk'))
- tasks.append(CompilationTask('OverlayAppSecond/Android.mk'))
- tasks.append(CompilationTask('OverlayAppFiltered/Android.mk'))
-
- # remount filesystem, install test project
- tasks.append(RootTask())
- tasks.append(RemountTask())
- tasks.append(PushTask('/system/app/OverlayTest/OverlayTest.apk', '/system/app/OverlayTest.apk'))
-
- # test idmap
- if opts.test_idmap:
- idmaps='/data/local/tmp/idmaps'
- symlinks='/data/local/tmp/symlinks'
-
- # idmap --path
- tasks.append(StopTask())
- tasks.append(_create_setup_idmap_path_task(idmaps, symlinks))
- tasks.append(StartTask())
- tasks.append(IdmapPathTask('/vendor/overlay/framework_a.apk', '/system/framework/framework-res.apk', idmaps + '/a.idmap'))
- tasks.append(FileExistsTest(idmaps + '/a.idmap'))
- tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1))
-
- # idmap --scan
- tasks.append(StopTask())
- tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks))
- tasks.append(StartTask())
- tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks))
- tasks.append(FileExistsTest(idmaps + '/vendor@overlay@framework_b.apk@idmap'))
- tasks.append(GrepIdmapTest(idmaps + '/vendor@overlay@framework_b.apk@idmap', 'bool/config_annoy_dianne', 1))
-
-
- # overlays.list
- overlays_list_path = idmaps + '/overlays.list'
- expected_content = '''\
-/vendor/overlay/framework_b.apk /data/local/tmp/idmaps/vendor@overlay@framework_b.apk@idmap
-'''
- tasks.append(FileExistsTest(overlays_list_path))
- tasks.append(Md5Test(overlays_list_path, expected_content))
-
- # idmap cleanup
- tasks.append(RmTask(symlinks))
- tasks.append(RmTask(idmaps))
-
- # test no overlay: all overlays cleared
- if opts.test_no_overlay:
- tasks.append(StopTask())
- tasks.append(_create_disable_overlays_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest'))
-
- # test single overlay: one overlay (a)
- if opts.test_single_overlay:
- tasks.append(StopTask())
- tasks.append(_create_enable_single_overlay_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest'))
-
- # test multiple overlays: all overlays - including 'disabled' filtered
- # overlay (system property unset) so expect 'b[p=2]' overrides 'a[p=1]' but
- # 'c[p=3]' should be ignored
- if opts.test_multiple_overlays:
- tasks.append(StopTask())
- tasks.append(_create_enable_multiple_overlays_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest'))
-
- # test filtered overlays: all overlays - including 'enabled' filtered
- # overlay (system property set/matched) so expect c[p=3] to override both a
- # & b where applicable
- if opts.test_filtered_overlays:
- tasks.append(StopTask())
- tasks.append(_create_enable_filtered_overlays_task())
- tasks.append(StartTask())
- tasks.append(InstrumentationTask('com.android.overlaytest.WithFilteredOverlaysTest'))
-
- ignored_errors = 0
- for t in tasks:
- type = t.get_type()
- name = t.get_name()
- if type == TASK_INSTRUMENTATION:
- # InstrumentationTask will run several tests, but we want it
- # to appear as if each test was run individually. Calling
- # "am instrument" with a single test method is prohibitively
- # expensive, so let's instead post-process the output to
- # emulate individual calls.
- retcode, stdout, stderr = t.execute()
- if retcode != 0:
- printer.begin(TASK_INSTRUMENTATION, name)
- printer.end_fail(TASK_INSTRUMENTATION, name, stderr)
- sys.exit(retcode)
- retcode = _handle_instrumentation_task_output(stdout, printer)
- if retcode != 0:
- if not opts.do_continue:
- sys.exit(retcode)
- else:
- ignored_errors += retcode
- else:
- printer.begin(type, name)
- retcode, stdout, stderr = t.execute()
- if retcode == 0:
- printer.end_pass(type, name)
- if retcode != 0:
- if len(stderr) == 0:
- # hope for output from stdout instead (true for eg adb shell rm)
- stderr = stdout
- printer.end_fail(type, name, stderr)
- if not opts.do_continue:
- sys.exit(retcode)
- else:
- ignored_errors += retcode
- sys.exit(ignored_errors)
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 04006b1..3021555 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -62,7 +62,6 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
<group gid="media_rw" />
- <group gid="sdcard_rw" />
</permission>
<permission name="android.permission.ACCESS_MTP" >
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8addffbb..3495b84 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -145,6 +145,7 @@
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
<permission name="android.permission.BIND_IMS_SERVICE"/>
+ <permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
<permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
@@ -227,6 +228,7 @@
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index dad24da..22867df 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -333,6 +333,9 @@
<family lang="und-Cari">
<font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
</family>
+ <family lang="und-Cakm">
+ <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font>
+ </family>
<family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
@@ -429,6 +432,9 @@
<family lang="und-Orkh">
<font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
</family>
+ <family lang="und-Osge">
+ <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
+ </family>
<family lang="und-Osma">
<font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
</family>
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1ef74ba..d0565ca 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -472,11 +472,15 @@
### Non-printing keys ###
key ESCAPE {
- base: fallback BACK
+ base: none
alt, meta: fallback HOME
ctrl: fallback MENU
}
+key DEL {
+ ctrl+alt: fallback BACK
+}
+
### Gamepad buttons ###
key BUTTON_A {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index c4647e0..c763cc09 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -469,11 +469,15 @@
### Non-printing keys ###
key ESCAPE {
- base: fallback BACK
+ base: none
alt, meta: fallback HOME
ctrl: fallback MENU
}
+key DEL {
+ ctrl+alt: fallback BACK
+}
+
### Gamepad buttons ###
key BUTTON_A {
diff --git a/docs/html/reference/images/text/style/drawablemarginspan.png b/docs/html/reference/images/text/style/drawablemarginspan.png
new file mode 100644
index 0000000..edf926d
--- /dev/null
+++ b/docs/html/reference/images/text/style/drawablemarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/dynamicdrawablespan.png b/docs/html/reference/images/text/style/dynamicdrawablespan.png
new file mode 100644
index 0000000..8776b03
--- /dev/null
+++ b/docs/html/reference/images/text/style/dynamicdrawablespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/iconmarginspan.png b/docs/html/reference/images/text/style/iconmarginspan.png
new file mode 100644
index 0000000..8ec39be
--- /dev/null
+++ b/docs/html/reference/images/text/style/iconmarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/imagespan.png b/docs/html/reference/images/text/style/imagespan.png
new file mode 100644
index 0000000..c03e6bb
--- /dev/null
+++ b/docs/html/reference/images/text/style/imagespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/maskfilterspan.png b/docs/html/reference/images/text/style/maskfilterspan.png
new file mode 100644
index 0000000..6e55dbc
--- /dev/null
+++ b/docs/html/reference/images/text/style/maskfilterspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/stylespan.png b/docs/html/reference/images/text/style/stylespan.png
new file mode 100644
index 0000000..9ffa05b
--- /dev/null
+++ b/docs/html/reference/images/text/style/stylespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/tabstopspan.png b/docs/html/reference/images/text/style/tabstopspan.png
new file mode 100644
index 0000000..89a1121
--- /dev/null
+++ b/docs/html/reference/images/text/style/tabstopspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png
new file mode 100644
index 0000000..67e2cf9
--- /dev/null
+++ b/docs/html/reference/images/text/style/typefacespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/urlspan.png b/docs/html/reference/images/text/style/urlspan.png
new file mode 100644
index 0000000..1134520
--- /dev/null
+++ b/docs/html/reference/images/text/style/urlspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 627d551..69a5874 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -541,10 +541,19 @@
return mAllowHwBitmapsInSwMode;
}
+ /**
+ * @hide
+ */
+ protected void onHwBitmapInSwMode() {
+ if (!mAllowHwBitmapsInSwMode) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support hardware bitmaps");
+ }
+ }
+
private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
- if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
- && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+ if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+ onHwBitmapInSwMode();
}
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0072012..44e7066 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
import libcore.util.NativeAllocationRegistry;
import java.io.OutputStream;
@@ -1171,6 +1175,82 @@
}
/**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+ * width and height the same as the Picture's width and height and a Config.HARDWARE
+ * config.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+ return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+ }
+
+ /**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * The bitmap will be immutable with the given width and height. If the width and height
+ * are not the same as the Picture's width & height, the Picture will be scaled to
+ * fit the given width and height.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @param width The width of the bitmap to create. The picture's width will be
+ * scaled to match if necessary.
+ * @param height The height of the bitmap to create. The picture's height will be
+ * scaled to match if necessary.
+ * @param config The {@link Config} of the created bitmap. If this is null then
+ * the bitmap will be {@link Config#HARDWARE}.
+ *
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+ @NonNull Config config) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width & height must be > 0");
+ }
+ if (config == null) {
+ throw new IllegalArgumentException("Config must not be null");
+ }
+ if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+ StrictMode.noteSlowCall("GPU readback");
+ }
+ if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+ final RenderNode node = RenderNode.create("BitmapTemporary", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+ final DisplayListCanvas canvas = node.start(width, height);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ node.end(canvas);
+ Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+ if (config != Config.HARDWARE) {
+ bitmap = bitmap.copy(config, false);
+ }
+ return bitmap;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+ Canvas canvas = new Canvas(bitmap);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ canvas.setBitmap(null);
+ bitmap.makeImmutable();
+ return bitmap;
+ }
+ }
+
+ /**
* Returns an optional array of private data, used by the UI system for
* some bitmaps. Not intended to be called by applications.
*/
@@ -1259,6 +1339,12 @@
return mIsMutable;
}
+ /** @hide */
+ public final void makeImmutable() {
+ // todo mIsMutable = false;
+ // todo nMakeImmutable();
+ }
+
/**
* <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
* When a pixel is pre-multiplied, the RGB components have been multiplied by
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index f5bf754..7ea35e7 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,6 +18,8 @@
import static android.graphics.BitmapFactory.Options.validate;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
@@ -518,8 +520,9 @@
* is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
* function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
- public static Bitmap decodeResourceStream(Resources res, TypedValue value,
- InputStream is, Rect pad, Options opts) {
+ @Nullable
+ public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
+ @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
validate(opts);
if (opts == null) {
opts = new Options();
@@ -707,7 +710,9 @@
* <code>is.mark(1024)</code> would be called. As of
* {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
- public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+ @Nullable
+ public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
+ @Nullable Options opts) {
// we don't throw in this case, thus allowing the caller to only check
// the cache, and not force the image to be decoded.
if (is == null) {
@@ -742,7 +747,8 @@
* Private helper function for decoding an InputStream natively. Buffers the input enough to
* do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
*/
- private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
+ private static Bitmap decodeStreamInternal(@NonNull InputStream is,
+ @Nullable Rect outPadding, @Nullable Options opts) {
// ASSERT(is != null);
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index f5e8633..d925441 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1219,10 +1219,14 @@
nFreeTextLayoutCaches();
}
+ /** @hide */
+ public static void setCompatibilityVersion(int apiLevel) { nSetCompatibilityVersion(apiLevel); }
+
private static native void nFreeCaches();
private static native void nFreeTextLayoutCaches();
private static native long nInitRaster(Bitmap bitmap);
private static native long nGetNativeFinalizer();
+ private static native void nSetCompatibilityVersion(int apiLevel);
// ---------------- @FastNative -------------------
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index d77e601..fe2b523 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -160,25 +160,6 @@
isItalic);
}
- /**
- * Allow creating unsupported FontFamily.
- *
- * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
- * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
- * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
- * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
- * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
- * the top of the fallback chain but is never used. if we don't create this empty FontFamily
- * and put it at top, bad things (performance regressions, unexpected glyph selection) will
- * happen.
- */
- public void allowUnsupportedFont() {
- if (mBuilderPtr == 0) {
- throw new IllegalStateException("Unable to allow unsupported font.");
- }
- nAllowUnsupportedFont(mBuilderPtr);
- }
-
// TODO: Remove once internal user stop using private API.
private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,9 +171,6 @@
private static native long nCreateFamily(long mBuilderPtr);
@CriticalNative
- private static native void nAllowUnsupportedFont(long builderPtr);
-
- @CriticalNative
private static native void nAbort(long mBuilderPtr);
@CriticalNative
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index bbf2145..acefead 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -74,7 +74,7 @@
int getDensity() { return Bitmap.DENSITY_NONE; }
/* @hide */
- int computeDstDensity() {
+ final int computeDstDensity() {
Resources res = getResources();
if (res == null) {
return Bitmap.getDefaultDensity();
@@ -122,13 +122,19 @@
}
private static class ContentResolverSource extends Source {
- ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
+ ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri,
+ @Nullable Resources res) {
mResolver = resolver;
mUri = uri;
+ mResources = res;
}
private final ContentResolver mResolver;
private final Uri mUri;
+ private final Resources mResources;
+
+ @Nullable
+ Resources getResources() { return mResources; }
@Override
public ImageDecoder createImageDecoder() throws IOException {
@@ -438,6 +444,7 @@
private boolean mPreferRamOverQuality = false;
private boolean mAsAlphaMask = false;
private Rect mCropRect;
+ private Rect mOutPaddingRect;
private Source mSource;
private PostProcessor mPostProcessor;
@@ -511,7 +518,18 @@
@NonNull
public static Source createSource(@NonNull ContentResolver cr,
@NonNull Uri uri) {
- return new ContentResolverSource(cr, uri);
+ return new ContentResolverSource(cr, uri, null);
+ }
+
+ /**
+ * Provide Resources for density scaling.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Source createSource(@NonNull ContentResolver cr,
+ @NonNull Uri uri, @Nullable Resources res) {
+ return new ContentResolverSource(cr, uri, res);
}
/**
@@ -765,6 +783,18 @@
}
/**
+ * Set a Rect for retrieving nine patch padding.
+ *
+ * If the image is a nine patch, this Rect will be set to the padding
+ * rectangle during decode. Otherwise it will not be modified.
+ *
+ * @hide
+ */
+ public void setOutPaddingRect(@NonNull Rect outPadding) {
+ mOutPaddingRect = outPadding;
+ }
+
+ /**
* Specify whether the {@link Bitmap} should be mutable.
*
* <p>By default, a {@link Bitmap} created will be immutable, but that can
@@ -875,7 +905,6 @@
postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mRequireUnpremultiplied,
mPreferRamOverQuality, mAsAlphaMask);
-
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -948,7 +977,10 @@
if (np != null && NinePatch.isNinePatchChunk(np)) {
Rect opticalInsets = new Rect();
bm.getOpticalInsets(opticalInsets);
- Rect padding = new Rect();
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding == null) {
+ padding = new Rect();
+ }
nGetPadding(decoder.mNativePtr, padding);
return new NinePatchDrawable(res, bm, np, padding,
opticalInsets, null);
@@ -991,6 +1023,15 @@
final int srcDensity = computeDensity(src, decoder);
Bitmap bm = decoder.decodeBitmap();
bm.setDensity(srcDensity);
+
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding != null) {
+ byte[] np = bm.getNinePatchChunk();
+ if (np != null && NinePatch.isNinePatchChunk(np)) {
+ nGetPadding(decoder.mNativePtr, padding);
+ }
+ }
+
return bm;
}
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 08eeaff..9ac94d8 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,8 +31,9 @@
* be replayed on a hardware accelerated canvas.</p>
*/
public class Picture {
- private Canvas mRecordingCanvas;
+ private PictureCanvas mRecordingCanvas;
private long mNativePicture;
+ private boolean mRequiresHwAcceleration;
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
@@ -78,8 +79,12 @@
* into it.
*/
public Canvas beginRecording(int width, int height) {
+ if (mRecordingCanvas != null) {
+ throw new IllegalStateException("Picture already recording, must call #endRecording()");
+ }
long ni = nativeBeginRecording(mNativePicture, width, height);
- mRecordingCanvas = new RecordingCanvas(this, ni);
+ mRecordingCanvas = new PictureCanvas(this, ni);
+ mRequiresHwAcceleration = false;
return mRecordingCanvas;
}
@@ -91,6 +96,7 @@
*/
public void endRecording() {
if (mRecordingCanvas != null) {
+ mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
mRecordingCanvas = null;
nativeEndRecording(mNativePicture);
}
@@ -113,6 +119,18 @@
}
/**
+ * Indicates whether or not this Picture contains recorded commands that only work when
+ * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+ * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+ *
+ * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+ * false otherwise.
+ */
+ public boolean requiresHardwareAcceleration() {
+ return mRequiresHwAcceleration;
+ }
+
+ /**
* Draw this picture on the canvas.
* <p>
* Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@
if (mRecordingCanvas != null) {
endRecording();
}
+ if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+ canvas.onHwBitmapInSwMode();
+ }
nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
@@ -164,8 +185,7 @@
if (stream == null) {
throw new NullPointerException();
}
- if (!nativeWriteToStream(mNativePicture, stream,
- new byte[WORKING_STREAM_STORAGE])) {
+ if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
throw new RuntimeException();
}
}
@@ -182,10 +202,11 @@
OutputStream stream, byte[] storage);
private static native void nativeDestructor(long nativePicture);
- private static class RecordingCanvas extends Canvas {
+ private static class PictureCanvas extends Canvas {
private final Picture mPicture;
+ boolean mHoldsHwBitmap;
- public RecordingCanvas(Picture pict, long nativeCanvas) {
+ public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
mPicture = pict;
}
@@ -202,5 +223,10 @@
}
super.drawPicture(picture);
}
+
+ @Override
+ protected void onHwBitmapInSwMode() {
+ mHoldsHwBitmap = true;
+ }
}
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index ef41507..f41267e 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -60,6 +60,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
@@ -801,39 +802,18 @@
* @return The new typeface.
*/
public static Typeface createFromAsset(AssetManager mgr, String path) {
- if (path == null) {
- throw new NullPointerException(); // for backward compatibility
- }
- synchronized (sDynamicCacheLock) {
- Typeface typeface = new Builder(mgr, path).build();
- if (typeface != null) return typeface;
+ Preconditions.checkNotNull(path); // for backward compatibility
+ Preconditions.checkNotNull(mgr);
- final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
- null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
- DEFAULT_FAMILY);
- typeface = sDynamicTypefaceCache.get(key);
- if (typeface != null) return typeface;
-
- final FontFamily fontFamily = new FontFamily();
- if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
- 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
- null /* axes */)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback
- // font selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
- final FontFamily[] families = { fontFamily };
- typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
- sDynamicTypefaceCache.put(key, typeface);
- return typeface;
- } else {
- fontFamily.abortCreation();
- }
+ Typeface typeface = new Builder(mgr, path).build();
+ if (typeface != null) return typeface;
+ // check if the file exists, and throw an exception for backward compatibility
+ try (InputStream inputStream = mgr.open(path)) {
+ } catch (IOException e) {
+ throw new RuntimeException("Font asset not found " + path);
}
- throw new RuntimeException("Font asset not found " + path);
+
+ return Typeface.DEFAULT;
}
/**
@@ -851,13 +831,22 @@
/**
* Create a new typeface from the specified font file.
*
- * @param path The path to the font data.
+ * @param file The path to the font data.
* @return The new typeface.
*/
- public static Typeface createFromFile(@Nullable File path) {
+ public static Typeface createFromFile(@Nullable File file) {
// For the compatibility reasons, leaving possible NPE here.
// See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
- return createFromFile(path.getAbsolutePath());
+
+ Typeface typeface = new Builder(file).build();
+ if (typeface != null) return typeface;
+
+ // check if the file exists, and throw an exception for backward compatibility
+ if (!file.exists()) {
+ throw new RuntimeException("Font asset not found " + file.getAbsolutePath());
+ }
+
+ return Typeface.DEFAULT;
}
/**
@@ -867,22 +856,8 @@
* @return The new typeface.
*/
public static Typeface createFromFile(@Nullable String path) {
- final FontFamily fontFamily = new FontFamily();
- if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback font
- // selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
- FontFamily[] families = { fontFamily };
- return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
- } else {
- fontFamily.abortCreation();
- }
- throw new RuntimeException("Font not found " + path);
+ Preconditions.checkNotNull(path); // for backward compatibility
+ return createFromFile(new File(path));
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index bd49b87..86e6fa8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -81,12 +81,7 @@
private State mState;
- private Runnable mRunnable = new Runnable() {
- @Override
- public void run() {
- invalidateSelf();
- }
- };
+ private Runnable mRunnable;
/**
* Pass this to {@link #setLoopCount} to loop infinitely.
@@ -242,6 +237,9 @@
// a value <= 0 indicates that the drawable is stopped or that renderThread
// will manage the animation
if (nextUpdate > 0) {
+ if (mRunnable == null) {
+ mRunnable = this::invalidateSelf;
+ }
scheduleSelf(mRunnable, nextUpdate);
} else if (nextUpdate == FINISHED) {
// This means the animation was drawn in software mode and ended.
@@ -348,7 +346,9 @@
if (mState == null) {
throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
}
- nStop(mState.mNativePtr);
+ if (nStop(mState.mNativePtr)) {
+ postOnAnimationEnd();
+ }
}
// Animatable2 overrides
@@ -365,21 +365,31 @@
nSetOnAnimationEndListener(mState.mNativePtr, this);
}
- mAnimationCallbacks.add(callback);
+ if (!mAnimationCallbacks.contains(callback)) {
+ mAnimationCallbacks.add(callback);
+ }
}
@Override
public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
- if (callback == null || mAnimationCallbacks == null) {
+ if (callback == null || mAnimationCallbacks == null
+ || !mAnimationCallbacks.remove(callback)) {
return false;
}
- return mAnimationCallbacks.remove(callback);
+ if (mAnimationCallbacks.isEmpty()) {
+ clearAnimationCallbacks();
+ }
+
+ return true;
}
@Override
public void clearAnimationCallbacks() {
- mAnimationCallbacks = null;
+ if (mAnimationCallbacks != null) {
+ mAnimationCallbacks = null;
+ nSetOnAnimationEndListener(mState.mNativePtr, null);
+ }
}
private void postOnAnimationStart() {
@@ -413,6 +423,21 @@
return mHandler;
}
+ /**
+ * Called by JNI.
+ *
+ * The JNI code has already posted this to the thread that created the
+ * callback, so no need to post.
+ */
+ @SuppressWarnings("unused")
+ private void onAnimationEnd() {
+ if (mAnimationCallbacks != null) {
+ for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+ callback.onAnimationEnd(this);
+ }
+ }
+ }
+
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
@@ -432,7 +457,7 @@
@FastNative
private static native boolean nStart(long nativePtr);
@FastNative
- private static native void nStop(long nativePtr);
+ private static native boolean nStop(long nativePtr);
@FastNative
private static native void nSetLoopCount(long nativePtr, int loopCount);
// Pass the drawable down to native so it can call onAnimationEnd.
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 7ad062a..44b783b 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Outline;
@@ -49,6 +50,7 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -111,7 +113,7 @@
*/
@Deprecated
public BitmapDrawable() {
- mBitmapState = new BitmapState((Bitmap) null);
+ init(new BitmapState((Bitmap) null), null);
}
/**
@@ -124,8 +126,7 @@
@SuppressWarnings("unused")
@Deprecated
public BitmapDrawable(Resources res) {
- mBitmapState = new BitmapState((Bitmap) null);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState((Bitmap) null), res);
}
/**
@@ -135,7 +136,7 @@
*/
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
- this(new BitmapState(bitmap), null);
+ init(new BitmapState(bitmap), null);
}
/**
@@ -143,8 +144,7 @@
* the display metrics of the resources.
*/
public BitmapDrawable(Resources res, Bitmap bitmap) {
- this(new BitmapState(bitmap), res);
- mBitmapState.mTargetDensity = mTargetDensity;
+ init(new BitmapState(bitmap), res);
}
/**
@@ -154,10 +154,7 @@
*/
@Deprecated
public BitmapDrawable(String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
- }
+ this(null, filepath);
}
/**
@@ -165,10 +162,21 @@
*/
@SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, String filepath) {
- this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ Bitmap bitmap = null;
+ try (FileInputStream stream = new FileInputStream(filepath)) {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+ (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeFile()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+ }
}
}
@@ -179,10 +187,7 @@
*/
@Deprecated
public BitmapDrawable(java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
- }
+ this(null, is);
}
/**
@@ -190,10 +195,21 @@
*/
@SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, java.io.InputStream is) {
- this(new BitmapState(BitmapFactory.decodeStream(is)), null);
- mBitmapState.mTargetDensity = mTargetDensity;
- if (mBitmapState.mBitmap == null) {
- android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ Bitmap bitmap = null;
+ try {
+ bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+ (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (Exception e) {
+ /* do nothing. This matches the behavior of BitmapFactory.decodeStream()
+ If the exception happened on decode, mBitmapState.mBitmap will be null.
+ */
+ } finally {
+ init(new BitmapState(bitmap), res);
+ if (mBitmapState.mBitmap == null) {
+ android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+ }
}
}
@@ -812,9 +828,19 @@
}
}
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+
Bitmap bitmap = null;
try (InputStream is = r.openRawResource(srcResId, value)) {
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
} catch (Exception e) {
// Do nothing and pick up the error below.
}
@@ -1013,14 +1039,21 @@
}
}
+ private BitmapDrawable(BitmapState state, Resources res) {
+ init(state, res);
+ }
+
/**
- * The one constructor to rule them all. This is called by all public
+ * The one helper to rule them all. This is called by all public & private
* constructors to set the state and initialize local properties.
*/
- private BitmapDrawable(BitmapState state, Resources res) {
+ private void init(BitmapState state, Resources res) {
mBitmapState = state;
-
updateLocalState(res);
+
+ if (mBitmapState != null && res != null) {
+ mBitmapState.mTargetDensity = mTargetDensity;
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index f17cd76..8af2fd8 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -50,11 +51,13 @@
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -1168,13 +1171,21 @@
/**
* Create a drawable from an inputstream, using the given resources and
* value to determine density information.
+ *
+ * @deprecated Prefer the version without an Options object.
*/
- public static Drawable createFromResourceStream(Resources res, TypedValue value,
- InputStream is, String srcName, BitmapFactory.Options opts) {
+ @Nullable
+ public static Drawable createFromResourceStream(@Nullable Resources res,
+ @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName,
+ @Nullable BitmapFactory.Options opts) {
if (is == null) {
return null;
}
+ if (opts == null) {
+ return getBitmapDrawable(res, value, is);
+ }
+
/* ugh. The decodeStream contract is that we have already allocated
the pad rect, but if the bitmap does not had a ninepatch chunk,
then the pad will be ignored. If we could change this to lazily
@@ -1190,7 +1201,6 @@
// an application in compatibility mode, without scaling those down
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
- if (opts == null) opts = new BitmapFactory.Options();
opts.inScreenDensity = Drawable.resolveDensity(res, 0);
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
@@ -1207,6 +1217,33 @@
return null;
}
+ private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+ try {
+ ImageDecoder.Source source = null;
+ if (value != null) {
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ source = ImageDecoder.createSource(res, is, density);
+ } else {
+ source = ImageDecoder.createSource(res, is);
+ }
+
+ return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException e) {
+ /* do nothing.
+ If the exception happened on decode, the drawable will be null.
+ */
+ Log.e("Drawable", "Unable to decode stream: " + e);
+ }
+ return null;
+ }
+
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
@@ -1306,11 +1343,10 @@
}
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
- try {
- Bitmap bm = BitmapFactory.decodeFile(pathName);
- if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, null, pathName);
- }
+ try (FileInputStream stream = new FileInputStream(pathName)) {
+ return getBitmapDrawable(null, null, stream);
+ } catch(IOException e) {
+ // Do nothing; we will just return null if the FileInputStream had an error
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f5a6f49..8b5114c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -825,6 +825,14 @@
mFillPaint.setXfermode(mode);
}
+ /**
+ * @param aa to draw this drawable with
+ * @hide
+ */
+ public void setAntiAlias(boolean aa) {
+ mFillPaint.setAntiAlias(aa);
+ }
+
private void buildPathIfDirty() {
final GradientState st = mGradientState;
if (mPathIsDirty) {
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 1790020..a56e8d1 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -24,9 +24,9 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
@@ -211,7 +211,8 @@
restoreAlpha = -1;
}
- final boolean needsDensityScaling = canvas.getDensity() == 0;
+ final boolean needsDensityScaling = canvas.getDensity() == 0
+ && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
if (needsDensityScaling) {
restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
@@ -421,10 +422,6 @@
final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
if (srcResId != 0) {
- final BitmapFactory.Options options = new BitmapFactory.Options();
- options.inDither = !state.mDither;
- options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
final Rect padding = new Rect();
final Rect opticalInsets = new Rect();
Bitmap bitmap = null;
@@ -433,7 +430,17 @@
final TypedValue value = new TypedValue();
final InputStream is = r.openRawResource(srcResId, value);
- bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+ int density = Bitmap.DENSITY_NONE;
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ density = value.density;
+ }
+ ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+ bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+ decoder.setOutPaddingRect(padding);
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
is.close();
} catch (IOException e) {
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 4129868..a8dc34a 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -110,6 +110,7 @@
// Take 60% of the maximum of the width and height, then divided half to get the radius.
mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
+ clampStartingPosition();
}
@Override
@@ -350,7 +351,7 @@
final float cY = mBounds.exactCenterY();
final float dX = mStartingX - cX;
final float dY = mStartingY - cY;
- final float r = mTargetRadius;
+ final float r = mTargetRadius - mStartRadius;
if (dX * dX + dY * dY > r * r) {
// Point is outside the circle, clamp to the perimeter.
final double angle = Math.atan2(dY, dX);
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 251b2e7..70d5216 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -145,6 +145,7 @@
"tests/TypeWrappers_test.cpp",
"tests/ZipUtils_test.cpp",
],
+ static_libs: ["libgmock"],
target: {
android: {
srcs: [
@@ -171,6 +172,7 @@
// Actual benchmarks.
"tests/AssetManager2_bench.cpp",
+ "tests/AttributeResolution_bench.cpp",
"tests/SparseEntry_bench.cpp",
"tests/Theme_bench.cpp",
],
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 415d3e3..a8c916b 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 invalidate_caches) {
apk_assets_ = apk_assets;
BuildDynamicRefTable();
+ RebuildFilterList();
if (invalidate_caches) {
InvalidateCaches(static_cast<uint32_t>(-1));
}
@@ -74,12 +100,14 @@
if (idx == 0xff) {
package_ids_[package_id] = idx = static_cast<uint8_t>(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<ApkAssetsCookie>(i));
// Add the package name -> build time ID mappings.
@@ -94,7 +122,7 @@
// 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::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,52 +198,54 @@
configuration_ = configuration;
if (diff) {
+ RebuildFilterList();
InvalidateCaches(static_cast<uint32_t>(diff));
}
}
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
- bool exclude_mipmap) {
+ bool exclude_mipmap) const {
ATRACE_CALL();
std::set<ResTable_config> 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<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
- bool merge_equivalent_languages) {
+ bool merge_equivalent_languages) const {
ATRACE_CALL();
std::set<std::string> 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<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
+std::unique_ptr<Asset> 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<Asset> 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<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
+std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const {
ATRACE_CALL();
std::string full_path = "assets/" + dirname;
@@ -236,7 +279,7 @@
// is inconsistent for split APKs.
std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
Asset::AccessMode mode,
- ApkAssetsCookie* out_cookie) {
+ ApkAssetsCookie* out_cookie) const {
ATRACE_CALL();
for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
@@ -255,7 +298,8 @@
}
std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
- ApkAssetsCookie cookie, Asset::AccessMode mode) {
+ ApkAssetsCookie cookie,
+ Asset::AccessMode mode) const {
ATRACE_CALL();
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return {};
@@ -264,14 +308,13 @@
}
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,53 +328,135 @@
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<ResTable_config>& 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 (best_cookie == kInvalidCookie) {
+ if (UNLIKELY(best_cookie == kInvalidCookie)) {
return kInvalidCookie;
}
- *out_entry = best_entry;
+ const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
+ if (UNLIKELY(best_entry == nullptr)) {
+ return kInvalidCookie;
+ }
+
+ 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) {
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
ATRACE_CALL();
FindEntryResult entry;
@@ -341,7 +466,8 @@
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 +495,7 @@
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,7 +509,7 @@
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) {
+ uint32_t* out_flags) const {
ATRACE_CALL();
FindEntryResult entry;
@@ -402,7 +528,7 @@
// 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 +540,7 @@
// 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 +548,14 @@
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) {
+ uint32_t* out_last_reference) const {
ATRACE_CALL();
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);
@@ -492,7 +616,8 @@
// 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 +649,8 @@
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 +669,8 @@
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 +709,8 @@
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 +766,7 @@
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 +798,8 @@
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 +821,32 @@
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<FilteredConfigGroup>();
+
+ // 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.
@@ -872,7 +1027,7 @@
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 60e3845..f912af4 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -20,13 +20,18 @@
#include <log/log.h>
+#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<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
+}
+
class XmlAttributeFinder
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
@@ -44,58 +49,53 @@
};
class BagAttributeFinder
- : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
+ : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
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 @@
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 @@
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 @@
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 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = -1;
+ cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -179,9 +182,7 @@
// 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<uint32_t>(res.getTableCookie(block))
- : static_cast<uint32_t>(-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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
}
} 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 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
+ cookie = kInvalidCookie;
}
if (kDebugStyles) {
@@ -385,9 +377,7 @@
// 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<uint32_t>(res.getTableCookie(block))
- : static_cast<uint32_t>(-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 @@
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 @@
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<uint32_t>(res->getTableCookie(block))
- : static_cast<uint32_t>(-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 @@
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 28548e2..1d2c597 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -44,44 +44,6 @@
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<TypeSpec>;
-
namespace {
// Builder that helps accumulate Type structs and then create a single
@@ -95,21 +57,22 @@
}
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<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
+ using ElementType = const ResTable_type*;
+ if ((std::numeric_limits<size_t>::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 @@
const ResTable_typeSpec* header_;
const IdmapEntry_header* idmap_header_;
- std::vector<Type> types_;
+ std::vector<const ResTable_type*> types_;
};
} // namespace
@@ -162,18 +125,17 @@
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<uint32_t>::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 @@
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 @@
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 @@
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 @@
reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(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 @@
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;
+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);
+}
- 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;
+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);
- 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.
- // 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<const ResTable_sparseTypeEntry*>(
- reinterpret_cast<const uint8_t*>(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 uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+ 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<const ResTable_sparseTypeEntry*>(
reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- const uint32_t offset = dtohl(entry_offsets[entry_idx]);
- if (offset == ResTable_type::NO_ENTRY) {
- continue;
- }
+ 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;
+ });
- // There is an entry for this resource, record it.
- best_offset = offset;
- }
-
- best_config = &type->configuration;
- best_type = type_chunk;
+ if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
+ // No entry found.
+ return ResTable_type::NO_ENTRY;
}
+
+ // 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 (best_type == nullptr) {
- 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;
}
- if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) {
- return false;
- }
-
- const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart));
-
- const uint32_t* flags = reinterpret_cast<const uint32_t*>(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<const uint32_t*>(
+ reinterpret_cast<const uint8_t*>(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<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
+ offset + dtohl(type_chunk->entriesStart));
}
void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
@@ -348,7 +267,7 @@
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<TypeSpec>& 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 @@
}
}
- 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 @@
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<TypeSpec>& 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 @@
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<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
+ reinterpret_cast<const uint8_t*>(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<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) +
- dtohl(type->type->entriesStart) + offset);
+ const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
if (dtohl(entry->key.index) == static_cast<uint32_t>(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 @@
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();
@@ -682,26 +605,6 @@
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) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index b033137..ef08897 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -69,6 +69,8 @@
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 @@
// If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
// will be excluded from the list.
std::set<ResTable_config> 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 @@
// If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
// and de-duped in the resulting list.
std::set<std::string> 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<Asset> Open(const std::string& filename, Asset::AccessMode mode);
+ std::unique_ptr<Asset> 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<Asset> 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<AssetDir> OpenDir(const std::string& dirname);
+ std::unique_ptr<AssetDir> 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 @@
//
// NOTE: The loaded APKs are searched in reverse order.
std::unique_ptr<Asset> 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<Asset> 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 @@
// 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 @@
// 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 @@
// 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 @@
std::unique_ptr<Theme> NewTheme();
template <typename Func>
- 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 @@
// 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 @@
// 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<const ApkAssets*> apk_assets_;
+ // A collection of configurations and their associated ResTable_type that match the current
+ // AssetManager configuration.
+ struct FilteredConfigGroup {
+ std::vector<ResTable_config> configurations;
+ std::vector<const ResTable_type*> 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<FilteredConfigGroup> 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<const LoadedPackage*> packages_;
+ // The set of packages that make-up this group.
+ std::vector<ConfiguredPackage> packages_;
+
+ // The cookies associated with each package in the group. They share the same order as
+ // packages_.
std::vector<ApkAssetsCookie> cookies_;
+
+ // A library reference table that contains build-package ID to runtime-package ID mappings.
DynamicRefTable dynamic_ref_table;
};
@@ -350,7 +382,7 @@
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 f281921..03fad49 100644
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ b/libs/androidfw/include/androidfw/AttributeFinder.h
@@ -58,6 +58,7 @@
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 @@
return end_;
}
+template <typename Derived, typename Iterator>
+Iterator BackTrackingAttributeFinder<Derived, Iterator>::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 69b76041..35ef98d 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 <androidfw/ResourceTypes.h>
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
namespace android {
@@ -42,19 +43,19 @@
// `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 965e2db..35ae5fc 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -41,32 +41,40 @@
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;
+// 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;
- // The configuration for which the resulting entry was defined.
- const ResTable_config* config = nullptr;
+ // 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;
- // Stores the resulting bitmask of configuration axis with which the resource value varies.
- uint32_t type_flags = 0u;
+ // The number of types that follow this struct.
+ // There is a type for each configuration that entries are defined for.
+ size_t type_count;
- // The dynamic package ID map for the package from which this resource came from.
- const DynamicRefTable* dynamic_ref_table = nullptr;
+ // Trick to easily access a variable number of Type structs
+ // proceeding this struct, and to ensure their alignment.
+ const ResTable_type* types[0];
- // 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;
+ inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
+ if (entry_index >= dtohl(type_spec->entryCount)) {
+ return 0u;
+ }
- // 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;
+ const uint32_t* flags = reinterpret_cast<const uint32_t*>(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<TypeSpec>;
class LoadedPackage {
public:
@@ -76,9 +84,6 @@
~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 @@
// 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 @@
// before being inserted into the set. This may cause some equivalent locales to de-dupe.
void CollectLocales(bool canonicalize, std::set<std::string>* 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 <typename Func>
+ 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<TypeSpec>& 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 @@
bool system_ = false;
bool overlay_ = false;
- ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
+ ByteBucketArray<TypeSpecPtr> type_specs_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
};
@@ -180,25 +209,20 @@
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 name of the package in `resid`, or nullptr if the package doesn't exist.
- const LoadedPackage* GetPackageForId(uint32_t resid) const;
-
- // Returns true if this is a system provided resource.
- inline bool IsSystem() const {
- return system_;
- }
+ // 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;
// Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
return packages_;
}
+ // Returns true if this is a system provided resource.
+ inline bool IsSystem() const {
+ return system_;
+ }
+
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 0000000..64924f4
--- /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 <mutex>
+#include <type_traits>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+template <typename T>
+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<T> 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<std::string> shared_string("hello");
+// {
+// ScopedLock<std::string> locked_string(shared_string);
+// *locked_string += " world";
+// }
+//
+template <typename T>
+class Guarded {
+ static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
+
+ public:
+ explicit Guarded() : guarded_() {
+ }
+
+ template <typename U = T>
+ explicit Guarded(const T& guarded,
+ typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
+ : guarded_(guarded) {
+ }
+
+ template <typename U = T>
+ explicit Guarded(T&& guarded,
+ typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
+ : guarded_(std::move(guarded)) {
+ }
+
+ private:
+ friend class ScopedLock<T>;
+
+ DISALLOW_COPY_AND_ASSIGN(Guarded);
+
+ std::mutex lock_;
+ T guarded_;
+};
+
+template <typename T>
+class ScopedLock {
+ public:
+ explicit ScopedLock(Guarded<T>& 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<std::mutex> 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 c2eae85..d94779b 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -28,7 +28,7 @@
StringPiece* out_entry);
inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
- return resid | (static_cast<uint32_t>(package_id) << 24);
+ return (resid & 0x00ffffffu) | (static_cast<uint32_t>(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 6c43a67..e2b9f00 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<const ApkAssets> 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> 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<const ApkAssets> 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> 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<const ApkAssets> 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 @@
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<void> 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 @@
// Open something so that the destructor of TemporaryFile closes a valid fd.
tf.fd = open("/dev/null", O_WRONLY);
- std::unique_ptr<const ApkAssets> 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<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_NE(nullptr, loaded_apk);
+ ASSERT_THAT(loaded_apk, NotNull());
- {
- std::unique_ptr<Asset> 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<Asset> 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<const ApkAssets> 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 @@
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 85e8f25..437e147 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -81,17 +81,18 @@
}
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 @@
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 @@
}
BENCHMARK(BM_AssetManagerGetResourceLocalesOld);
+static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) {
+ std::unique_ptr<const ApkAssets> 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 92462a6..eaf79cb 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -59,7 +59,7 @@
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*/);
@@ -233,6 +233,25 @@
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(
+ {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
ASSERT_NE(nullptr, bag);
ASSERT_GE(bag->entry_count, 2u);
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
new file mode 100644
index 0000000..fa300c5
--- /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<const ApkAssets> 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> 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> theme = assetmanager.NewTheme();
+ theme->ApplyStyle(app::R::style::StyleTwo);
+
+ std::array<uint32_t, 6> 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<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size() + 1> 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<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath);
+ if (framework_apk == nullptr) {
+ state.SkipWithError("failed to load framework assets");
+ return;
+ }
+
+ std::unique_ptr<const ApkAssets> 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> 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> theme = assetmanager.NewTheme();
+ theme->ApplyStyle(Theme_Material_Light);
+
+ std::array<uint32_t, 92> 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<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size() + 1> 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 2d73ce8..c8dbe20 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 @@
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<const ApkAssets> styles_assets_;
+ AssetManager2 assetmanager_;
};
class AttributeResolutionXmlTest : public AttributeResolutionTest {
@@ -48,13 +49,12 @@
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> 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 @@
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> theme = assetmanager.NewTheme();
+
+ std::array<uint32_t, 2> attrs{
+ {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size() + 1> 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> theme = assetmanager_.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> 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 @@
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> 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, ThemeAndXmlParser) {
- ResTable::Theme theme(table_);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+ std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
std::array<uint32_t, 6> 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<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> 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 7149bee..faddfe5 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -33,19 +33,21 @@
}
}
+ // 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 @@
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 37ddafb..bedebd6 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -16,6 +16,8 @@
#include "androidfw/LoadedArsc.h"
+#include "androidfw/ResourceUtils.h"
+
#include "TestHelpers.h"
#include "data/basic/R.h"
#include "data/libclient/R.h"
@@ -27,6 +29,13 @@
namespace libclient = com::android::libclient;
namespace sparse = com::android::sparse;
+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 +44,24 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
- const std::vector<std::unique_ptr<const LoadedPackage>>& 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());
+ 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));
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 24;
+ 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);
- FindEntryResult entry;
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
- 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));
-
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
-
- ResTable_config desired_config;
- memset(&desired_config, 0, sizeof(desired_config));
- desired_config.language[0] = 'd';
- desired_config.language[1] = 'e';
-
- 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 +70,22 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_THAT(loaded_arsc, NotNull());
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.sdkVersion = 26;
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
+ ASSERT_THAT(package, NotNull());
- FindEntryResult entry;
- ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry));
- ASSERT_NE(nullptr, entry.entry);
+ 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);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+
+ 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 +94,13 @@
&contents));
std::unique_ptr<const LoadedArsc> 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 +114,23 @@
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> 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 +141,12 @@
std::unique_ptr<const LoadedArsc> 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 +154,27 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
&contents));
std::unique_ptr<const LoadedArsc> 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());
}
class MockLoadedIdmap : public LoadedIdmap {
@@ -199,23 +202,33 @@
};
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<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap);
- ASSERT_NE(nullptr, loaded_arsc);
+ LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
+ ASSERT_THAT(loaded_arsc, NotNull());
- ResTable_config desired_config;
- memset(&desired_config, 0, sizeof(desired_config));
+ const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
+ ASSERT_THAT(package, NotNull());
- FindEntryResult entry;
- ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry));
+ 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 43a9955..df0c642 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -20,6 +20,7 @@
#include <string>
#include "androidfw/ResourceTypes.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "CommonHelpers.h"
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 94a2a14..b7e814f 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -34,6 +34,7 @@
struct layout {
enum : uint32_t {
main = 0x7f020000,
+ layoutt = 0x7f020001,
};
};
@@ -55,6 +56,7 @@
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 18ef75e..1733b6a 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files 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 0000000..045ede4
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/ok"
+ android:layout_width="0sp"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_marginStart="2dip"
+ android:layout_marginEnd="2dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:text="@android:string/ok" />
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 6c47459..b343562 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -22,6 +22,7 @@
<attr name="attr2" format="reference|integer" />
<public type="layout" name="main" id="0x7f020000" />
+ <public type="layout" name="layout" id="0x7f020001" />
<public type="string" name="test1" id="0x7f030000" />
<string name="test1">test1</string>
@@ -43,6 +44,18 @@
<public type="integer" name="ref2" id="0x7f040003" />
<integer name="ref2">12000</integer>
+ <public type="integer" name="deep_ref" id="0x7f040004" />
+ <integer name="deep_ref">@integer/deep_ref_1</integer>
+ <integer name="deep_ref_1">@integer/deep_ref_2</integer>
+ <integer name="deep_ref_2">@integer/deep_ref_3</integer>
+ <integer name="deep_ref_3">@integer/deep_ref_4</integer>
+ <integer name="deep_ref_4">@integer/deep_ref_5</integer>
+ <integer name="deep_ref_5">@integer/deep_ref_6</integer>
+ <integer name="deep_ref_6">@integer/deep_ref_7</integer>
+ <integer name="deep_ref_7">@integer/deep_ref_8</integer>
+ <integer name="deep_ref_8">@integer/deep_ref_9</integer>
+ <integer name="deep_ref_9">100</integer>
+
<public type="style" name="Theme1" id="0x7f050000" />
<style name="Theme1">
<item name="com.android.basic:attr1">100</item>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3323bce..24d819e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -134,6 +134,8 @@
name: "libhwui_defaults",
defaults: ["hwui_defaults"],
+ shared_libs: ["libstatslog"],
+
whole_static_libs: ["libskia"],
srcs: [
@@ -318,7 +320,10 @@
"libgmock",
"libhwui_static_debug",
],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
cflags: [
"-include debug/wrap_gles.h",
"-DHWUI_NULL_GPU",
@@ -383,7 +388,10 @@
// set to libhwui_static_debug to skip actual GL commands
whole_static_libs: ["libhwui"],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
srcs: [
"tests/macrobench/TestSceneRunner.cpp",
@@ -405,7 +413,10 @@
],
whole_static_libs: ["libhwui_static_debug"],
- shared_libs: ["libmemunreachable"],
+ shared_libs: [
+ "libmemunreachable",
+ "libstatslog",
+ ],
srcs: [
"tests/microbench/main.cpp",
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index f41956c..ab27a0d 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -18,6 +18,7 @@
#include <errno.h>
#include <inttypes.h>
+#include <statslog.h>
#include <sys/mman.h>
#include <algorithm>
@@ -164,6 +165,7 @@
ALOGI("%s", ss.str().c_str());
// Just so we have something that counts up, the value is largely irrelevant
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
+ android::util::stats_write(android::util::DAVEY_OCCURRED, ns2ms(totalDuration));
}
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index b2edd33..2b0b22d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -187,11 +187,6 @@
static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) {
SkCanvas::SaveLayerFlags layerFlags = 0;
- // We intentionally ignore the SaveFlags::HasAlphaLayer and
- // SkCanvas::kIsOpaque_SaveLayerFlag flags because HWUI ignores it
- // and our Android client may use it incorrectly.
- // In Skia, this flag is purely for performance optimization.
-
if (!(flags & SaveFlags::ClipToLayer)) {
layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag;
}
@@ -747,6 +742,12 @@
SkPaint paintCopy(paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
+ // older.
+ if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0
+ && paintCopy.getStyle() == SkPaint::kStroke_Style) {
+ paintCopy.setStyle(SkPaint::kFill_Style);
+ }
SkRect bounds =
SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 06e2d6c..fc009d8 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -204,10 +204,6 @@
saveFlags |= SaveFlags::ClipToLayer;
}
- if (!(layerFlags & SkCanvas::kIsOpaque_SaveLayerFlag)) {
- saveFlags |= SaveFlags::HasAlphaLayer;
- }
-
return saveFlags;
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index f118e8d..f662406 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -557,13 +557,12 @@
mAtlasKey = INVALID_ATLAS_KEY;
}
-void Tree::draw(SkCanvas* canvas) {
+void Tree::draw(SkCanvas* canvas, const SkRect& bounds) {
SkRect src;
sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
if (vdSurface) {
canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src,
- mutateProperties()->getBounds(), getPaint(),
- SkCanvas::kFast_SrcRectConstraint);
+ bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
} else {
// Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
// We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
@@ -575,8 +574,7 @@
int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight),
- mutateProperties()->getBounds(), getPaint(),
- SkCanvas::kFast_SrcRectConstraint);
+ bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
mCache.clear();
markDirty();
}
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index d9cf8ab..da52a95 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -644,7 +644,7 @@
* Draws VD cache into a canvas. This should always be called from RT and it works with Skia
* pipelines only.
*/
- void draw(SkCanvas* canvas);
+ void draw(SkCanvas* canvas, const SkRect& bounds);
/**
* Draws VD into a GPU backed surface.
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 5356d3b..2bded9b 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -48,8 +48,10 @@
return true;
}
-void AnimatedImageDrawable::stop() {
+bool AnimatedImageDrawable::stop() {
+ bool wasRunning = mRunning;
mRunning = false;
+ return wasRunning;
}
bool AnimatedImageDrawable::isRunning() {
@@ -180,7 +182,6 @@
if (finalFrame) {
if (mEndListener) {
mEndListener->onAnimationEnd();
- mEndListener = nullptr;
}
}
}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 9d84ed5..2fd6f40 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -68,7 +68,9 @@
// Returns true if the animation was started; false otherwise (e.g. it was
// already running)
bool start();
- void stop();
+ // Returns true if the animation was stopped; false otherwise (e.g. it was
+ // already stopped)
+ bool stop();
bool isRunning();
void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 284fd83..ad4c8be 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -225,4 +225,10 @@
MinikinUtils::forFontRun(layout, &paintCopy, f);
}
+int Canvas::sApiLevel = 1;
+
+void Canvas::setCompatibilityVersion(int apiLevel) {
+ sApiLevel = apiLevel;
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 3ddf1c4..fabb8d2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -116,6 +116,14 @@
static Canvas* create_canvas(SkCanvas* skiaCanvas);
/**
+ * Sets the target SDK version used to build the app.
+ *
+ * @param apiLevel API level
+ *
+ */
+ static void setCompatibilityVersion(int apiLevel);
+
+ /**
* Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
* It is useful for testing and clients (e.g. Picture/Movie) that expect to
* draw their contents into an SkCanvas.
@@ -282,6 +290,8 @@
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const SkPaint& paint, const SkPath& path, size_t start,
size_t end) = 0;
+ static int sApiLevel;
+
friend class DrawTextFunctor;
friend class DrawTextOnPathFunctor;
friend class uirenderer::SkiaCanvasProxy;
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 43f46ef..a0d000d 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -44,7 +44,6 @@
minikinPaint.familyVariant = paint->getFamilyVariant();
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
return minikinPaint;
}
@@ -55,18 +54,23 @@
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
minikin::Layout layout;
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
if (mt == nullptr) {
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
- if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
- minikin::Range(start, start + count),
- minikinPaint, bidiFlags, mtOffset, &layout)) {
+ if (mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, mtOffset, startHyphen, endHyphen,
+ &layout)) {
return layout;
}
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf, range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
@@ -74,8 +78,14 @@
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
- return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
- advances, nullptr /* extent */);
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
+ return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
+ endHyphen, advances, nullptr /* extent */);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index ebc14c8..dca9ef5 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -132,8 +132,8 @@
bool italicFromFont;
const minikin::FontStyle defaultStyle;
- const minikin::MinikinFont* mf =
- families.empty() ? nullptr : families[0]->getClosestMatch(defaultStyle).font;
+ const minikin::MinikinFont* mf = families.empty() ? nullptr
+ : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
if (mf != nullptr) {
SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
const SkFontStyle& style = skTypeface->fontStyle();
@@ -182,10 +182,11 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
- std::shared_ptr<minikin::FontCollection> collection =
- std::make_shared<minikin::FontCollection>(std::move(family));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font::Builder(font).build());
+
+ std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
+ std::make_shared<minikin::FontFamily>(std::move(fonts)));
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index eabe2e8..25c76eb 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -124,14 +124,19 @@
class VectorDrawable : public SkDrawable {
public:
- VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
+ VectorDrawable(VectorDrawableRoot* tree)
+ : mRoot(tree)
+ , mBounds(tree->stagingProperties()->getBounds()) {}
protected:
- virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); }
- virtual void onDraw(SkCanvas* canvas) override { mRoot->draw(canvas); }
+ virtual SkRect onGetBounds() override { return mBounds; }
+ virtual void onDraw(SkCanvas* canvas) override {
+ mRoot->draw(canvas, mBounds);
+ }
private:
sp<VectorDrawableRoot> mRoot;
+ SkRect mBounds;
};
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2953ea8..182e364 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1143,4 +1143,47 @@
RenderNodeDrawable drawable(parent.get(), &canvas, false);
canvas.drawDrawable(&drawable);
EXPECT_EQ(6, canvas.getIndex());
-}
\ No newline at end of file
+}
+
+// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+ static const int CANVAS_WIDTH = 100;
+ static const int CANVAS_HEIGHT = 200;
+ class VectorDrawableTestCanvas : public TestCanvasBase {
+ public:
+ VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+ void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint) override {
+ const int index = mDrawCounter++;
+ switch (index) {
+ case 0:
+ EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
+ break;
+ case 1:
+ EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+ };
+
+ VectorDrawable::Group* group = new VectorDrawable::Group();
+ sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
+ vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
+
+ auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
+ CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
+ CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ });
+
+ VectorDrawableTestCanvas canvas;
+ RenderNodeDrawable drawable(node.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(2, canvas.mDrawCounter);
+}
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 66d6f52..e424a26 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -56,8 +56,9 @@
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- return std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font::Builder(font).build());
+ return std::make_shared<minikin::FontFamily>(std::move(fonts));
}
std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 1904d40..f24abae 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -249,15 +249,15 @@
}
uint32_t id = (uint32_t)fieldId;
+ size_t prevPos = mBuffer.wp()->pos();
mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
-
size_t sizePos = mBuffer.wp()->pos();
mDepth++;
mObjectId++;
mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack.
- mExpectedObjectToken = makeToken(get_varint_size(id),
+ mExpectedObjectToken = makeToken(sizePos - prevPos,
(bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos);
return mExpectedObjectToken;
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 018db9a..fa3f99a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -88,11 +88,6 @@
boolean providerMeetsCriteria(String provider, in Criteria criteria);
ProviderProperties getProviderProperties(String provider);
String getNetworkProviderPackage();
- boolean isProviderEnabled(String provider);
- boolean isProviderEnabledForUser(String provider, int userId);
- boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
- boolean isLocationEnabledForUser(int userId);
- void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c33dce1..d194796 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -236,6 +236,62 @@
*/
public static final String GNSS_HARDWARE_MODEL_NAME_UNKNOWN = "Model Name Unknown";
+ /**
+ * Broadcast intent action for Settings app to inject a footer at the bottom of location
+ * settings.
+ *
+ * <p>This broadcast is used for two things:
+ * <ol>
+ * <li>For receivers to inject a footer with provided text. This is for use only by apps
+ * that are included in the system image. </li>
+ * <li>For receivers to know their footer is injected under location settings.</li>
+ * </ol>
+ *
+ * <p>To inject a footer to location settings, you must declare a broadcast receiver of
+ * {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} in the manifest as so:
+ * <pre>
+ * <receiver android:name="com.example.android.footer.MyFooterInjector">
+ * <intent-filter>
+ * <action android:name="com.android.settings.location.INJECT_FOOTER" />
+ * </intent-filter>
+ * <meta-data
+ * android:name="com.android.settings.location.FOOTER_STRING"
+ * android:resource="@string/my_injected_footer_string" />
+ * </receiver>
+ * </pre>
+ *
+ * <p>On entering location settings, Settings app will send a
+ * {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast to receivers whose footer is successfully
+ * injected. On leaving location settings, the footer becomes not visible to users. Settings app
+ * will send a {@link #SETTINGS_FOOTER_REMOVED_ACTION} broadcast to those receivers.
+ *
+ * @hide
+ */
+ public static final String SETTINGS_FOOTER_DISPLAYED_ACTION =
+ "com.android.settings.location.DISPLAYED_FOOTER";
+
+ /**
+ * Broadcast intent action when location settings footer is not visible to users.
+ *
+ * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use.
+ *
+ * @hide
+ */
+ public static final String SETTINGS_FOOTER_REMOVED_ACTION =
+ "com.android.settings.location.REMOVED_FOOTER";
+
+ /**
+ * Metadata name for {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast
+ * receivers to specify a string resource id as location settings footer text. This is for use
+ * only by apps that are included in the system image.
+ *
+ * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use.
+ *
+ * @hide
+ */
+ public static final String METADATA_SETTINGS_FOOTER_STRING =
+ "com.android.settings.location.FOOTER_STRING";
+
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap<LocationListener,ListenerTransport> mListeners =
new HashMap<LocationListener,ListenerTransport>();
@@ -1215,6 +1271,9 @@
final String allowedProviders = Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
userHandle.getIdentifier());
+ if (allowedProviders == null) {
+ return false;
+ }
final List<String> providerList = Arrays.asList(allowedProviders.split(","));
for(String provider : getAllProviders()) {
if (provider.equals(PASSIVE_PROVIDER)) {
diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java
index 2d974ee..76876ce 100644
--- a/lowpan/java/android/net/lowpan/LowpanManager.java
+++ b/lowpan/java/android/net/lowpan/LowpanManager.java
@@ -57,7 +57,7 @@
* This design pattern allows us to skip removal of items
* from this Map without leaking memory.
*/
- private final Map<ILowpanInterface, WeakReference<LowpanInterface>> mBinderCache =
+ private final Map<IBinder, WeakReference<LowpanInterface>> mBinderCache =
new WeakHashMap<>();
private final ILowpanManager mService;
@@ -109,13 +109,27 @@
/** @hide */
@Nullable
+ public LowpanInterface getInterfaceNoCreate(@NonNull ILowpanInterface ifaceService) {
+ LowpanInterface iface = null;
+
+ synchronized (mBinderCache) {
+ if (mBinderCache.containsKey(ifaceService.asBinder())) {
+ iface = mBinderCache.get(ifaceService.asBinder()).get();
+ }
+ }
+
+ return iface;
+ }
+
+ /** @hide */
+ @Nullable
public LowpanInterface getInterface(@NonNull ILowpanInterface ifaceService) {
LowpanInterface iface = null;
try {
synchronized (mBinderCache) {
- if (mBinderCache.containsKey(ifaceService)) {
- iface = mBinderCache.get(ifaceService).get();
+ if (mBinderCache.containsKey(ifaceService.asBinder())) {
+ iface = mBinderCache.get(ifaceService.asBinder()).get();
}
if (iface == null) {
@@ -127,7 +141,7 @@
mInterfaceCache.put(iface.getName(), iface);
}
- mBinderCache.put(ifaceService, new WeakReference(iface));
+ mBinderCache.put(ifaceService.asBinder(), new WeakReference(iface));
/* Make sure we remove the object from the
* interface cache if the associated service
@@ -260,7 +274,7 @@
public void onInterfaceRemoved(ILowpanInterface ifaceService) {
Runnable runnable =
() -> {
- LowpanInterface iface = getInterface(ifaceService);
+ LowpanInterface iface = getInterfaceNoCreate(ifaceService);
if (iface != null) {
cb.onInterfaceRemoved(iface);
diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 5d0c8e2..5467a69 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -38,6 +38,10 @@
private int mLossReceived;
private int mFlags;
+ // generation count for the validity of a request/response async exchange between
+ // external focus policy and MediaFocusControl
+ private long mGenCount = -1;
+
/**
* Class constructor
@@ -61,6 +65,16 @@
mSdkTarget = sdk;
}
+ /** @hide */
+ public void setGen(long g) {
+ mGenCount = g;
+ }
+
+ /** @hide */
+ public long getGen() {
+ return mGenCount;
+ }
+
/**
* The audio attributes for the audio focus request.
@@ -128,6 +142,7 @@
dest.writeInt(mLossReceived);
dest.writeInt(mFlags);
dest.writeInt(mSdkTarget);
+ dest.writeLong(mGenCount);
}
@Override
@@ -168,6 +183,8 @@
if (mSdkTarget != other.mSdkTarget) {
return false;
}
+ // mGenCount is not used to verify equality between two focus holds as multiple requests
+ // (hence of different generations) could correspond to the same hold
return true;
}
@@ -175,7 +192,7 @@
= new Parcelable.Creator<AudioFocusInfo>() {
public AudioFocusInfo createFromParcel(Parcel in) {
- return new AudioFocusInfo(
+ final AudioFocusInfo afi = new AudioFocusInfo(
AudioAttributes.CREATOR.createFromParcel(in), //AudioAttributes aa
in.readInt(), // int clientUid
in.readString(), //String clientId
@@ -185,6 +202,8 @@
in.readInt(), //int flags
in.readInt() //int sdkTarget
);
+ afi.setGen(in.readLong());
+ return afi;
}
public AudioFocusInfo[] newArray(int size) {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d042..f98480b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -265,6 +265,12 @@
public static final int ENCODING_AAC_XHE = 16;
/** Audio data format: AC-4 sync frame transport format */
public static final int ENCODING_AC4 = 17;
+ /** Audio data format: E-AC-3-JOC compressed
+ * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}.
+ * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device
+ * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
+ **/
+ public static final int ENCODING_E_AC3_JOC = 18;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -512,6 +518,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -537,6 +544,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -564,6 +572,7 @@
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -593,6 +602,7 @@
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -829,6 +839,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -1044,6 +1055,7 @@
ENCODING_PCM_FLOAT,
ENCODING_AC3,
ENCODING_E_AC3,
+ ENCODING_E_AC3_JOC,
ENCODING_DTS,
ENCODING_DTS_HD,
ENCODING_IEC61937,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bf51d97..0be54ec 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.media.audiopolicy.AudioPolicy;
+import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionLegacyHelper;
@@ -54,10 +55,13 @@
import android.util.Slog;
import android.view.KeyEvent;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -2338,6 +2342,20 @@
}
}
}
+
+ @Override
+ public void dispatchFocusResultFromExtPolicy(int requestResult, String clientId) {
+ synchronized (mFocusRequestsLock) {
+ // TODO use generation counter as the key instead
+ final BlockingFocusResultReceiver focusReceiver =
+ mFocusRequestsAwaitingResult.remove(clientId);
+ if (focusReceiver != null) {
+ focusReceiver.notifyResult(requestResult);
+ } else {
+ Log.e(TAG, "dispatchFocusResultFromExtPolicy found no result receiver");
+ }
+ }
+ }
};
private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
@@ -2390,6 +2408,40 @@
*/
public static final int AUDIOFOCUS_REQUEST_DELAYED = 2;
+ /** @hide */
+ @IntDef(flag = false, prefix = "AUDIOFOCUS_REQUEST", value = {
+ AUDIOFOCUS_REQUEST_FAILED,
+ AUDIOFOCUS_REQUEST_GRANTED,
+ AUDIOFOCUS_REQUEST_DELAYED }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FocusRequestResult {}
+
+ /**
+ * @hide
+ * code returned when a synchronous focus request on the client-side is to be blocked
+ * until the external audio focus policy decides on the response for the client
+ */
+ public static final int AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY = 100;
+
+ /**
+ * Timeout duration in ms when waiting on an external focus policy for the result for a
+ * focus request
+ */
+ private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 200;
+
+ private static final String FOCUS_CLIENT_ID_STRING = "android_audio_focus_client_id";
+
+ private final Object mFocusRequestsLock = new Object();
+ /**
+ * Map of all receivers of focus request results, one per unresolved focus request.
+ * Receivers are added before sending the request to the external focus policy,
+ * and are removed either after receiving the result, or after the timeout.
+ * This variable is lazily initialized.
+ */
+ @GuardedBy("mFocusRequestsLock")
+ private HashMap<String, BlockingFocusResultReceiver> mFocusRequestsAwaitingResult;
+
/**
* Request audio focus.
@@ -2656,18 +2708,100 @@
// some tests don't have a Context
sdk = Build.VERSION.SDK_INT;
}
- try {
- status = service.requestAudioFocus(afr.getAudioAttributes(),
- afr.getFocusGain(), mICallBack,
- mAudioFocusDispatcher,
- getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener()),
- getContext().getOpPackageName() /* package name */, afr.getFlags(),
- ap != null ? ap.cb() : null,
- sdk);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+
+ final String clientId = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
+ final BlockingFocusResultReceiver focusReceiver;
+ synchronized (mFocusRequestsLock) {
+ try {
+ // TODO status contains result and generation counter for ext policy
+ status = service.requestAudioFocus(afr.getAudioAttributes(),
+ afr.getFocusGain(), mICallBack,
+ mAudioFocusDispatcher,
+ clientId,
+ getContext().getOpPackageName() /* package name */, afr.getFlags(),
+ ap != null ? ap.cb() : null,
+ sdk);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
+ // default path with no external focus policy
+ return status;
+ }
+ if (mFocusRequestsAwaitingResult == null) {
+ mFocusRequestsAwaitingResult =
+ new HashMap<String, BlockingFocusResultReceiver>(1);
+ }
+ focusReceiver = new BlockingFocusResultReceiver(clientId);
+ mFocusRequestsAwaitingResult.put(clientId, focusReceiver);
}
- return status;
+ focusReceiver.waitForResult(EXT_FOCUS_POLICY_TIMEOUT_MS);
+ if (DEBUG && !focusReceiver.receivedResult()) {
+ Log.e(TAG, "requestAudio response from ext policy timed out, denying request");
+ }
+ synchronized (mFocusRequestsLock) {
+ mFocusRequestsAwaitingResult.remove(clientId);
+ }
+ return focusReceiver.requestResult();
+ }
+
+ // helper class that abstracts out the handling of spurious wakeups in Object.wait()
+ private static final class SafeWaitObject {
+ private boolean mQuit = false;
+
+ public void safeNotify() {
+ synchronized (this) {
+ mQuit = true;
+ this.notify();
+ }
+ }
+
+ public void safeWait(long millis) throws InterruptedException {
+ final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
+ synchronized (this) {
+ while (!mQuit) {
+ final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
+ if (timeToWait < 0) { break; }
+ this.wait(timeToWait);
+ }
+ }
+ }
+ }
+
+ private static final class BlockingFocusResultReceiver {
+ private final SafeWaitObject mLock = new SafeWaitObject();
+ @GuardedBy("mLock")
+ private boolean mResultReceived = false;
+ // request denied by default (e.g. timeout)
+ private int mFocusRequestResult = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ private final String mFocusClientId;
+
+ BlockingFocusResultReceiver(String clientId) {
+ mFocusClientId = clientId;
+ }
+
+ boolean receivedResult() { return mResultReceived; }
+ int requestResult() { return mFocusRequestResult; }
+
+ void notifyResult(int requestResult) {
+ synchronized (mLock) {
+ mResultReceived = true;
+ mFocusRequestResult = requestResult;
+ mLock.safeNotify();
+ }
+ }
+
+ public void waitForResult(long timeOutMs) {
+ synchronized (mLock) {
+ if (mResultReceived) {
+ // the result was received before waiting
+ return;
+ }
+ try {
+ mLock.safeWait(timeOutMs);
+ } catch (InterruptedException e) { }
+ }
+ }
}
/**
@@ -2714,6 +2848,32 @@
/**
* @hide
+ * Set the result to the audio focus request received through
+ * {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
+ * @param afi the information about the focus requester
+ * @param requestResult the result to the focus request to be passed to the requester
+ * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
+ @FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
+ if (afi == null) {
+ throw new IllegalArgumentException("Illegal null AudioFocusInfo");
+ }
+ if (ap == null) {
+ throw new IllegalArgumentException("Illegal null AudioPolicy");
+ }
+ final IAudioService service = getService();
+ try {
+ service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Notifies an application with a focus listener of gain or loss of audio focus.
* This method can only be used by owners of an {@link AudioPolicy} configured with
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
new file mode 100644
index 0000000..4652c18
--- /dev/null
+++ b/media/java/android/media/AudioPresentation.java
@@ -0,0 +1,181 @@
+/*
+ * 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.media;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * The AudioPresentation class encapsulates the information that describes an audio presentation
+ * which is available in next generation audio content.
+ *
+ * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
+ * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
+ * presentations and to select one.
+ *
+ * A list of available audio presentations in a media source can be queried using
+ * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
+ * selection.
+ * An AudioPresentation can be passed to an offloaded audio decoder via
+ * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected
+ * presentation. An audio stream may contain multiple presentations that differ by language,
+ * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have
+ * a set of description labels in different languages to help the user to make an informed
+ * selection.
+ */
+public final class AudioPresentation {
+ private final int mPresentationId;
+ private final int mProgramId;
+ private final Map<String, String> mLabels;
+ private final String mLanguage;
+
+ /** @hide */
+ @IntDef(
+ value = {
+ MASTERING_NOT_INDICATED,
+ MASTERED_FOR_STEREO,
+ MASTERED_FOR_SURROUND,
+ MASTERED_FOR_3D,
+ MASTERED_FOR_HEADPHONE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MasteringIndicationType {}
+
+ private final @MasteringIndicationType int mMasteringIndication;
+ private final boolean mAudioDescriptionAvailable;
+ private final boolean mSpokenSubtitlesAvailable;
+ private final boolean mDialogueEnhancementAvailable;
+
+ /**
+ * No preferred reproduction channel layout.
+ */
+ public static final int MASTERING_NOT_INDICATED = 0;
+ /**
+ * Stereo speaker layout.
+ */
+ public static final int MASTERED_FOR_STEREO = 1;
+ /**
+ * Two-dimensional (e.g. 5.1) speaker layout.
+ */
+ public static final int MASTERED_FOR_SURROUND = 2;
+ /**
+ * Three-dimensional (e.g. 5.1.2) speaker layout.
+ */
+ public static final int MASTERED_FOR_3D = 3;
+ /**
+ * Prerendered for headphone playback.
+ */
+ public static final int MASTERED_FOR_HEADPHONE = 4;
+
+ AudioPresentation(int presentationId,
+ int programId,
+ Map<String, String> labels,
+ String language,
+ @MasteringIndicationType int masteringIndication,
+ boolean audioDescriptionAvailable,
+ boolean spokenSubtitlesAvailable,
+ boolean dialogueEnhancementAvailable) {
+ this.mPresentationId = presentationId;
+ this.mProgramId = programId;
+ this.mLanguage = language;
+ this.mMasteringIndication = masteringIndication;
+ this.mAudioDescriptionAvailable = audioDescriptionAvailable;
+ this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+ this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+
+ this.mLabels = new HashMap<String, String>(labels);
+ }
+
+ /**
+ * The framework uses this presentation id to select an audio presentation rendered by a
+ * decoder. Presentation id is typically sequential, but does not have to be.
+ * @hide
+ */
+ public int getPresentationId() {
+ return mPresentationId;
+ }
+
+ /**
+ * The framework uses this program id to select an audio presentation rendered by a decoder.
+ * Program id can be used to further uniquely identify the presentation to a decoder.
+ * @hide
+ */
+ public int getProgramId() {
+ return mProgramId;
+ }
+
+ /**
+ * @return a map of available text labels for this presentation. Each label is indexed by its
+ * locale corresponding to the language code as specified by ISO 639-2 [42]. Either ISO 639-2/B
+ * or ISO 639-2/T could be used.
+ */
+ public Map<Locale, String> getLabels() {
+ Map<Locale, String> localeLabels = new HashMap<>();
+ for (Map.Entry<String, String> entry : mLabels.entrySet()) {
+ localeLabels.put(new Locale(entry.getKey()), entry.getValue());
+ }
+ return localeLabels;
+ }
+
+ /**
+ * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
+ */
+ public Locale getLocale() {
+ return new Locale(mLanguage);
+ }
+
+ /**
+ * @return the mastering indication of the audio presentation.
+ * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO},
+ * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE}
+ */
+ @MasteringIndicationType
+ public int getMasteringIndication() {
+ return mMasteringIndication;
+ }
+
+ /**
+ * Indicates whether an audio description for the visually impaired is available.
+ * @return {@code true} if audio description is available.
+ */
+ public boolean hasAudioDescription() {
+ return mAudioDescriptionAvailable;
+ }
+
+ /**
+ * Indicates whether spoken subtitles for the visually impaired are available.
+ * @return {@code true} if spoken subtitles are available.
+ */
+ public boolean hasSpokenSubtitles() {
+ return mSpokenSubtitlesAvailable;
+ }
+
+ /**
+ * Indicates whether dialogue enhancement is available.
+ * @return {@code true} if dialogue enhancement is available.
+ */
+ public boolean hasDialogueEnhancement() {
+ return mDialogueEnhancementAvailable;
+ }
+}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index d0963cb..3847530 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1416,6 +1416,7 @@
/*
* Call BEFORE adding a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
@@ -1425,6 +1426,7 @@
/*
* Call AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 4e9ce8e..2d5fad5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2008,6 +2008,25 @@
}
/**
+ * Sets the audio presentation.
+ * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned.
+ * If a multi-stream decoder (MSD) is not present, or the format does not support
+ * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned.
+ * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+ * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_INVALID_OPERATION}
+ * @throws IllegalArgumentException if the audio presentation is null.
+ * @throws IllegalStateException if track is not initialized.
+ */
+ public int setPresentation(@NonNull AudioPresentation presentation) {
+ if (presentation == null) {
+ throw new IllegalArgumentException("audio presentation is null");
+ }
+ return native_setPresentation(presentation.getPresentationId(),
+ presentation.getProgramId());
+ }
+
+ /**
* Sets the initialization state of the instance. This method was originally intended to be used
* in an AudioTrack subclass constructor to set a subclass-specific post-initialization state.
* However, subclasses of AudioTrack are no longer recommended, so this method is obsolete.
@@ -2802,6 +2821,7 @@
/*
* Call BEFORE adding a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
@@ -2811,6 +2831,7 @@
/*
* Call AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
@@ -3245,6 +3266,7 @@
@NonNull VolumeShaper.Operation operation);
private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+ private native final int native_setPresentation(int presentationId, int programId);
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/IAudioFocusDispatcher.aidl b/media/java/android/media/IAudioFocusDispatcher.aidl
index 09575f7..3b33c5b 100644
--- a/media/java/android/media/IAudioFocusDispatcher.aidl
+++ b/media/java/android/media/IAudioFocusDispatcher.aidl
@@ -25,4 +25,6 @@
void dispatchAudioFocusChange(int focusChange, String clientId);
+ void dispatchFocusResultFromExtPolicy(int requestResult, String clientId);
+
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 88d0a60..cd4143c 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -207,5 +207,8 @@
int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent);
+ oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
+ in IAudioPolicyCallback pcb);
+
// WARNING: read warning at top of file, it is recommended to add new methods at the end
}
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 5ad4313..5cb8313 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -19,7 +19,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.update.ApiLoader;
import android.media.update.MediaBrowser2Provider;
@@ -41,14 +40,14 @@
*/
public static class BrowserCallback extends MediaController2.ControllerCallback {
/**
- * Called with the result of {@link #getBrowserRoot(Bundle)}.
+ * Called with the result of {@link #getLibraryRoot(Bundle)}.
* <p>
- * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+ * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the library root isn't
* available.
*
* @param rootHints rootHints that you previously requested.
- * @param rootMediaId media id of the browser root. Can be {@code null}
- * @param rootExtra extra of the browser root. Can be {@code null}
+ * @param rootMediaId media id of the library root. Can be {@code null}
+ * @param rootExtra extra of the library root. Can be {@code null}
*/
public void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
@Nullable Bundle rootExtra) { }
@@ -114,8 +113,15 @@
.createMediaBrowser2(context, this, token, executor, (BrowserCallback) callback);
}
- public void getBrowserRoot(Bundle rootHints) {
- mProvider.getBrowserRoot_impl(rootHints);
+ /**
+ * Get the library root. Result would be sent back asynchronously with the
+ * {@link BrowserCallback#onGetRootResult(Bundle, String, Bundle)}.
+ *
+ * @param rootHints hint for the root
+ * @see BrowserCallback#onGetRootResult(Bundle, String, Bundle)
+ */
+ public void getLibraryRoot(Bundle rootHints) {
+ mProvider.getLibraryRoot_impl(rootHints);
}
/**
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index b32e539..bd6c7e6 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -30,7 +30,7 @@
import android.media.session.MediaSessionManager;
import android.media.update.ApiLoader;
import android.media.update.MediaController2Provider;
-import android.media.update.PlaybackInfoProvider;
+import android.media.update.MediaController2Provider.PlaybackInfoProvider;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -132,7 +132,7 @@
public void onPlaylistChanged(@NonNull List<MediaItem2> playlist) { }
/**
- * Called when the playback state is changed, or connection success.
+ * Called when the playback state is changed.
*
* @param state latest playback state
*/
@@ -172,6 +172,14 @@
}
/**
+ * @hide
+ */
+ @SystemApi
+ public PlaybackInfoProvider getProvider() {
+ return mProvider;
+ }
+
+ /**
* Get the type of playback which affects volume handling. One of:
* <ul>
* <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
@@ -199,9 +207,9 @@
/**
* Get the type of volume control that can be used. One of:
* <ul>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
+ * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
* </ul>
*
* @return The type of volume control that may be used with this session.
@@ -416,7 +424,7 @@
* @param extras Optional extras that can include extra information about the media item
* to be played.
*/
- public void playFromUri(@NonNull String uri, @Nullable Bundle extras) {
+ public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
mProvider.playFromUri_impl(uri, extras);
}
@@ -472,7 +480,7 @@
/**
* Set the volume of the output this session is playing on. The command will be ignored if it
- * does not support {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+ * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
* <p>
* If the session is local playback, this changes the device's volume with the stream that
* session's player is using. Flags will be specified for the {@link AudioManager}.
@@ -494,8 +502,8 @@
* must be one of {@link AudioManager#ADJUST_LOWER},
* {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
* The command will be ignored if the session does not support
- * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
- * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+ * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
+ * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
* <p>
* If the session is local playback, this changes the device's volume with the stream that
* session's player is using. Flags will be specified for the {@link AudioManager}.
diff --git a/media/java/android/media/MediaDataSource.java b/media/java/android/media/MediaDataSource.java
index 948da0b..4ba2120 100644
--- a/media/java/android/media/MediaDataSource.java
+++ b/media/java/android/media/MediaDataSource.java
@@ -34,8 +34,8 @@
/**
* Called to request data from the given position.
*
- * Implementations should should write up to {@code size} bytes into
- * {@code buffer}, and return the number of bytes written.
+ * Implementations should fill {@code buffer} with up to {@code size}
+ * bytes of data, and return the number of valid bytes in the buffer.
*
* Return {@code 0} if size is zero (thus no bytes are read).
*
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 174d6a3..4919eeb 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.AudioPresentation;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaHTTPService;
@@ -40,6 +41,7 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -396,6 +398,17 @@
}
/**
+ * Get the list of available audio presentations for the track.
+ * @param trackIndex index of the track.
+ * @return a list of available audio presentations for a given valid audio track index.
+ * The list will be empty if the source does not contain any audio presentations.
+ */
+ @NonNull
+ public List<AudioPresentation> getAudioPresentations(int trackIndex) {
+ return new ArrayList<AudioPresentation>();
+ }
+
+ /**
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
* the crypto scheme, and the bytes being the data specific to that scheme.
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index a901c68..a11768e 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -25,8 +25,8 @@
import android.media.MediaSession2.BuilderBase;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
-import android.media.update.MediaSession2Provider;
import android.media.update.MediaSessionService2Provider;
import android.os.Bundle;
@@ -62,7 +62,8 @@
public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
/**
- * Session for the media library service.
+ * Session for the {@link MediaLibraryService2}. Build this object with
+ * {@link MediaLibrarySessionBuilder} and return in {@link #onCreateSession(String)}.
*/
public static class MediaLibrarySession extends MediaSession2 {
private final MediaLibrarySessionProvider mProvider;
@@ -100,6 +101,9 @@
}
}
+ /**
+ * Callback for the {@link MediaLibrarySession}.
+ */
public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
public MediaLibrarySessionCallback(Context context) {
@@ -116,15 +120,15 @@
*
* @param controllerInfo information of the controller requesting access to browse media.
* @param rootHints An optional bundle of service-specific arguments to send
- * to the media browser service when connecting and retrieving the
+ * to the media library service when connecting and retrieving the
* root id for browsing, or null if none. The contents of this
* bundle may affect the information returned when browsing.
- * @return The {@link BrowserRoot} for accessing this app's content or null.
- * @see BrowserRoot#EXTRA_RECENT
- * @see BrowserRoot#EXTRA_OFFLINE
- * @see BrowserRoot#EXTRA_SUGGESTED
+ * @return The {@link LibraryRoot} for accessing this app's content or null.
+ * @see LibraryRoot#EXTRA_RECENT
+ * @see LibraryRoot#EXTRA_OFFLINE
+ * @see LibraryRoot#EXTRA_SUGGESTED
*/
- public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
+ public @Nullable LibraryRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
@Nullable Bundle rootHints) {
return null;
}
@@ -199,6 +203,8 @@
/**
* Builder for {@link MediaLibrarySession}.
*/
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession,
MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
public MediaLibrarySessionBuilder(
@@ -209,6 +215,38 @@
context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor,
callback));
}
+
+ @Override
+ public MediaLibrarySessionBuilder setVolumeProvider(
+ @Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setRatingType(int type) {
+ return super.setRatingType(type);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setId(String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionCallback(
+ @NonNull Executor executor, @NonNull MediaLibrarySessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaLibrarySession build() {
+ return super.build();
+ }
}
@Override
@@ -228,7 +266,7 @@
* This method will be called on the main thread.
*
* @param sessionId session id written in the AndroidManifest.xml.
- * @return a new browser session
+ * @return a new library session
* @see MediaLibrarySessionBuilder
* @see #getSession()
* @throws RuntimeException if returned session is invalid
@@ -237,17 +275,17 @@
public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
/**
- * Contains information that the browser service needs to send to the client
- * when first connected.
+ * Contains information that the library service needs to send to the client when
+ * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
*/
- public static final class BrowserRoot {
+ public static final class LibraryRoot {
/**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for recently played media items.
+ * The lookup key for a boolean that indicates whether the library service should return a
+ * librar root for recently played media items.
*
- * <p>When creating a media browser for a given media browser service, this key can be
+ * <p>When creating a media browser for a given media library service, this key can be
* supplied as a root hint for retrieving media items that are recently played.
- * If the media browser service can provide such media items, the implementation must return
+ * If the media library service can provide such media items, the implementation must return
* the key in the root hint when
* {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
*
@@ -259,13 +297,13 @@
public static final String EXTRA_RECENT = "android.media.extra.RECENT";
/**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for offline media items.
+ * The lookup key for a boolean that indicates whether the library service should return a
+ * library root for offline media items.
*
- * <p>When creating a media browser for a given media browser service, this key can be
+ * <p>When creating a media browser for a given media library service, this key can be
* supplied as a root hint for retrieving media items that are can be played without an
* internet connection.
- * If the media browser service can provide such media items, the implementation must return
+ * If the media library service can provide such media items, the implementation must return
* the key in the root hint when
* {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
*
@@ -277,14 +315,14 @@
public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
/**
- * The lookup key for a boolean that indicates whether the browser service should return a
- * browser root for suggested media items.
+ * The lookup key for a boolean that indicates whether the library service should return a
+ * library root for suggested media items.
*
- * <p>When creating a media browser for a given media browser service, this key can be
- * supplied as a root hint for retrieving the media items suggested by the media browser
+ * <p>When creating a media browser for a given media library service, this key can be
+ * supplied as a root hint for retrieving the media items suggested by the media library
* service. The list of media items is considered ordered by relevance, first being the top
* suggestion.
- * If the media browser service can provide such media items, the implementation must return
+ * If the media library service can provide such media items, the implementation must return
* the key in the root hint when
* {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
*
@@ -295,35 +333,31 @@
*/
public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
- final private String mRootId;
- final private Bundle mExtras;
+ private final LibraryRootProvider mProvider;
/**
- * Constructs a browser root.
+ * Constructs a library root.
* @param rootId The root id for browsing.
- * @param extras Any extras about the browser service.
+ * @param extras Any extras about the library service.
*/
- public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
- if (rootId == null) {
- throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
- "Use null for BrowserRoot instead.");
- }
- mRootId = rootId;
- mExtras = extras;
+ public LibraryRoot(@NonNull Context context,
+ @NonNull String rootId, @Nullable Bundle extras) {
+ mProvider = ApiLoader.getProvider(context).createMediaLibraryService2LibraryRoot(
+ context, this, rootId, extras);
}
/**
* Gets the root id for browsing.
*/
public String getRootId() {
- return mRootId;
+ return mProvider.getRootId_impl();
}
/**
- * Gets any extras about the browser service.
+ * Gets any extras about the library service.
*/
public Bundle getExtras() {
- return mExtras;
+ return mProvider.getExtras_impl();
}
}
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1bc3dfa..fe5e822 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1484,6 +1484,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d84eedf..e331b2c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1671,35 +1671,6 @@
public abstract void deselectTrack(int index);
/**
- * Sets the target UDP re-transmit endpoint for the low level player.
- * Generally, the address portion of the endpoint is an IP multicast
- * address, although a unicast address would be equally valid. When a valid
- * retransmit endpoint has been set, the media player will not decode and
- * render the media presentation locally. Instead, the player will attempt
- * to re-multiplex its media data using the Android@Home RTP profile and
- * re-transmit to the target endpoint. Receiver devices (which may be
- * either the same as the transmitting device or different devices) may
- * instantiate, prepare, and start a receiver player using a setDataSource
- * URL of the form...
- *
- * aahRX://<multicastIP>:<port>
- *
- * to receive, decode and render the re-transmitted content.
- *
- * setRetransmitEndpoint may only be called before setDataSource has been
- * called; while the player is in the Idle state.
- *
- * @param endpoint the address and UDP port of the re-transmission target or
- * null if no re-transmission is to be performed.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the retransmit endpoint is supplied,
- * but invalid.
- *
- * {@hide} pending API council
- */
- public void setRetransmitEndpoint(InetSocketAddress endpoint) { }
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 222c66e..d4e9aac 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -1417,6 +1417,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
@@ -2964,53 +2965,6 @@
}
/**
- * Sets the target UDP re-transmit endpoint for the low level player.
- * Generally, the address portion of the endpoint is an IP multicast
- * address, although a unicast address would be equally valid. When a valid
- * retransmit endpoint has been set, the media player will not decode and
- * render the media presentation locally. Instead, the player will attempt
- * to re-multiplex its media data using the Android@Home RTP profile and
- * re-transmit to the target endpoint. Receiver devices (which may be
- * either the same as the transmitting device or different devices) may
- * instantiate, prepare, and start a receiver player using a setDataSource
- * URL of the form...
- *
- * aahRX://<multicastIP>:<port>
- *
- * to receive, decode and render the re-transmitted content.
- *
- * setRetransmitEndpoint may only be called before setDataSource has been
- * called; while the player is in the Idle state.
- *
- * @param endpoint the address and UDP port of the re-transmission target or
- * null if no re-transmission is to be performed.
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if the retransmit endpoint is supplied,
- * but invalid.
- *
- * {@hide} pending API council
- */
- @Override
- public void setRetransmitEndpoint(InetSocketAddress endpoint)
- throws IllegalStateException, IllegalArgumentException
- {
- String addrString = null;
- int port = 0;
-
- if (null != endpoint) {
- addrString = endpoint.getAddress().getHostAddress();
- port = endpoint.getPort();
- }
-
- int ret = native_setRetransmitEndpoint(addrString, port);
- if (ret != 0) {
- throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
- }
- }
-
- private native final int native_setRetransmitEndpoint(String addrString, int port);
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 62240ce..823410f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1353,6 +1353,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index acf9615..943b827 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -31,6 +31,7 @@
import android.media.update.ApiLoader;
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
import android.media.update.MediaSession2Provider.CommandGroupProvider;
import android.media.update.MediaSession2Provider.CommandProvider;
import android.media.update.MediaSession2Provider.ControllerInfoProvider;
@@ -501,7 +502,7 @@
/**
* Called when a controller requested to play a specific media item represented by a URI
- * through {@link MediaController2#playFromUri(String, Bundle)}
+ * through {@link MediaController2#playFromUri(Uri, Bundle)}
*
* @param controller controller information
* @param uri uri
@@ -509,7 +510,7 @@
* @see #COMMAND_CODE_PLAY_FROM_URI
*/
public void onPlayFromUri(@NonNull ControllerInfo controller,
- @NonNull String uri, @Nullable Bundle extras) { }
+ @NonNull Uri uri, @Nullable Bundle extras) { }
/**
* Called when a controller requested to prepare for playing a specific mediaId through
@@ -579,8 +580,20 @@
};
/**
- * Base builder class for MediaSession2 and its subclass.
- *
+ * Base builder class for MediaSession2 and its subclass. Any change in this class should be
+ * also applied to the subclasses {@link MediaSession2.Builder} and
+ * {@link MediaLibraryService2.MediaLibrarySessionBuilder}.
+ * <p>
+ * APIs here should be package private, but should have documentations for developers.
+ * Otherwise, javadoc will generate documentation with the generic types such as follows.
+ * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre>
+ * <p>
+ * This class is hidden to prevent from generating test stub, which fails with
+ * 'unexpected bound' because it tries to auto generate stub class as follows.
+ * <pre>abstract static class BuilderBase<
+ * T extends android.media.MediaSession2,
+ * U extends android.media.MediaSession2.BuilderBase<
+ * T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre>
* @hide
*/
static abstract class BuilderBase
@@ -598,9 +611,9 @@
* <p>
* Set {@code null} to reset.
*
- * @param volumeProvider The provider that will handle volume changes. Can be {@code null}
+ * @param volumeProvider The provider that will handle volume changes. Can be {@code null}.
*/
- public U setVolumeProvider(@Nullable VolumeProvider volumeProvider) {
+ U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
mProvider.setVolumeProvider_impl(volumeProvider);
return (U) this;
}
@@ -618,7 +631,7 @@
* <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
* </ul>
*/
- public U setRatingType(@Rating2.Style int type) {
+ U setRatingType(@Rating2.Style int type) {
mProvider.setRatingType_impl(type);
return (U) this;
}
@@ -630,7 +643,7 @@
*
* @param pi The intent to launch to show UI for this session.
*/
- public U setSessionActivity(@Nullable PendingIntent pi) {
+ U setSessionActivity(@Nullable PendingIntent pi) {
mProvider.setSessionActivity_impl(pi);
return (U) this;
}
@@ -645,7 +658,7 @@
* @throws IllegalArgumentException if id is {@code null}
* @return
*/
- public U setId(@NonNull String id) {
+ U setId(@NonNull String id) {
mProvider.setId_impl(id);
return (U) this;
}
@@ -657,7 +670,7 @@
* @param callback session callback.
* @return
*/
- public U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
+ U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull C callback) {
mProvider.setSessionCallback_impl(executor, callback);
return (U) this;
@@ -670,7 +683,7 @@
* @throws IllegalStateException if the session with the same id is already exists for the
* package.
*/
- public T build() {
+ T build() {
return mProvider.build_impl();
}
}
@@ -681,13 +694,44 @@
* Any incoming event from the {@link MediaController2} will be handled on the thread
* that created session with the {@link Builder#build()}.
*/
- // TODO(jaewan): Add setRatingType()
- // TODO(jaewan): Add setSessionActivity()
+ // Override all methods just to show them with the type instead of generics in Javadoc.
+ // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
public Builder(Context context, @NonNull MediaPlayerInterface player) {
super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder(
context, (Builder) instance, player));
}
+
+ @Override
+ public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+ return super.setVolumeProvider(volumeProvider);
+ }
+
+ @Override
+ public Builder setRatingType(@Rating2.Style int type) {
+ return super.setRatingType(type);
+ }
+
+ @Override
+ public Builder setSessionActivity(@Nullable PendingIntent pi) {
+ return super.setSessionActivity(pi);
+ }
+
+ @Override
+ public Builder setId(@NonNull String id) {
+ return super.setId(id);
+ }
+
+ @Override
+ public Builder setSessionCallback(@NonNull Executor executor,
+ @Nullable SessionCallback callback) {
+ return super.setSessionCallback(executor, callback);
+ }
+
+ @Override
+ public MediaSession2 build() {
+ return super.build();
+ }
}
/**
@@ -768,32 +812,15 @@
* <p>
* It's up to the controller's decision to respect or ignore this customization request.
*/
- // TODO(jaewan): Move this to updatable.
public static class CommandButton {
- private static final String KEY_COMMAND
- = "android.media.media_session2.command_button.command";
- private static final String KEY_ICON_RES_ID
- = "android.media.media_session2.command_button.icon_res_id";
- private static final String KEY_DISPLAY_NAME
- = "android.media.media_session2.command_button.display_name";
- private static final String KEY_EXTRA
- = "android.media.media_session2.command_button.extra";
- private static final String KEY_ENABLED
- = "android.media.media_session2.command_button.enabled";
+ private final CommandButtonProvider mProvider;
- private Command mCommand;
- private int mIconResId;
- private String mDisplayName;
- private Bundle mExtra;
- private boolean mEnabled;
-
- private CommandButton(@Nullable Command command, int iconResId,
- @Nullable String displayName, Bundle extra, boolean enabled) {
- mCommand = command;
- mIconResId = iconResId;
- mDisplayName = displayName;
- mExtra = extra;
- mEnabled = enabled;
+ /**
+ * @hide
+ */
+ @SystemApi
+ public CommandButton(CommandButtonProvider provider) {
+ mProvider = provider;
}
/**
@@ -803,7 +830,7 @@
* @return command or {@code null}
*/
public @Nullable Command getCommand() {
- return mCommand;
+ return mProvider.getCommand_impl();
}
/**
@@ -813,7 +840,7 @@
* @return resource id of the icon. Can be {@code 0}.
*/
public int getIconResId() {
- return mIconResId;
+ return mProvider.getIconResId_impl();
}
/**
@@ -823,7 +850,7 @@
* @return custom display name. Can be {@code null} or empty.
*/
public @Nullable String getDisplayName() {
- return mDisplayName;
+ return mProvider.getDisplayName_impl();
}
/**
@@ -832,7 +859,7 @@
* @return
*/
public @Nullable Bundle getExtra() {
- return mExtra;
+ return mProvider.getExtra_impl();
}
/**
@@ -841,92 +868,50 @@
* @return {@code true} if enabled. {@code false} otherwise.
*/
public boolean isEnabled() {
- return mEnabled;
+ return mProvider.isEnabled_impl();
}
/**
* @hide
*/
- // TODO(jaewan): @SystemApi
- public @NonNull Bundle toBundle() {
- Bundle bundle = new Bundle();
- bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
- bundle.putInt(KEY_ICON_RES_ID, mIconResId);
- bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
- bundle.putBundle(KEY_EXTRA, mExtra);
- bundle.putBoolean(KEY_ENABLED, mEnabled);
- return bundle;
- }
-
- /**
- * @hide
- */
- // TODO(jaewan): @SystemApi
- public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
- Builder builder = new Builder();
- builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND)));
- builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
- builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
- builder.setExtra(bundle.getBundle(KEY_EXTRA));
- builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
- try {
- return builder.build();
- } catch (IllegalStateException e) {
- // Malformed or version mismatch. Return null for now.
- return null;
- }
+ @SystemApi
+ public CommandButtonProvider getProvider() {
+ return mProvider;
}
/**
* Builder for {@link CommandButton}.
*/
public static class Builder {
- private Command mCommand;
- private int mIconResId;
- private String mDisplayName;
- private Bundle mExtra;
- private boolean mEnabled;
+ private final CommandButtonProvider.BuilderProvider mProvider;
- public Builder() {
- mEnabled = true;
+ public Builder(@NonNull Context context) {
+ mProvider = ApiLoader.getProvider(context)
+ .createMediaSession2CommandButtonBuilder(context, this);
}
public Builder setCommand(Command command) {
- mCommand = command;
- return this;
+ return mProvider.setCommand_impl(command);
}
public Builder setIconResId(int resId) {
- mIconResId = resId;
- return this;
+ return mProvider.setIconResId_impl(resId);
}
public Builder setDisplayName(String displayName) {
- mDisplayName = displayName;
- return this;
+ return mProvider.setDisplayName_impl(displayName);
}
public Builder setEnabled(boolean enabled) {
- mEnabled = enabled;
- return this;
+ return mProvider.setEnabled_impl(enabled);
}
public Builder setExtra(Bundle extra) {
- mExtra = extra;
- return this;
+ return mProvider.setExtra_impl(extra);
}
public CommandButton build() {
- if (mEnabled && mCommand == null) {
- throw new IllegalStateException("Enabled button needs Command"
- + " for controller to invoke the command");
- }
- if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
- && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
- throw new IllegalStateException("Custom commands needs icon and"
- + " and name to display");
- }
- return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled);
+ return mProvider.build_impl();
}
}
}
@@ -1093,8 +1078,8 @@
* If the new player is successfully set, {@link PlaybackListener}
* will be called to tell the current playback state of the new player.
* <p>
- * You can also specify a volume provider. If so, playback in the player is considered as
- * remote playback.
+ * For the remote playback case which you want to handle volume by yourself, use
+ * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
*
* @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
* @throws IllegalArgumentException if the player is {@code null}.
@@ -1109,10 +1094,10 @@
* @param player a {@link MediaPlayerInterface} that handles actual media playback in your app.
* @param volumeProvider a volume provider
* @see #setPlayer(MediaPlayerInterface)
- * @see Builder#setVolumeProvider(VolumeProvider)
+ * @see Builder#setVolumeProvider(VolumeProvider2)
*/
public void setPlayer(@NonNull MediaPlayerInterface player,
- @NonNull VolumeProvider volumeProvider) {
+ @NonNull VolumeProvider2 volumeProvider) {
mProvider.setPlayer_impl(player, volumeProvider);
}
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 6b2de06..0b5dddf 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,10 +21,12 @@
import android.annotation.Nullable;
import android.app.Notification;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.media.MediaSession2.ControllerInfo;
import android.media.update.ApiLoader;
import android.media.update.MediaSessionService2Provider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.os.IBinder;
/**
@@ -165,7 +167,6 @@
* @param state playback state
* @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
*/
- // TODO(jaewan): Also add metadata
public MediaNotification onUpdateNotification(PlaybackState2 state) {
return mProvider.onUpdateNotification_impl(state);
}
@@ -204,31 +205,31 @@
* foreground service to keep playback running in the background. It's highly recommended to
* show media style notification here.
*/
- // TODO(jaewan): Should we also move this to updatable?
public static class MediaNotification {
- public final int id;
- public final Notification notification;
-
- private MediaNotification(int id, @NonNull Notification notification) {
- this.id = id;
- this.notification = notification;
- }
+ private final MediaNotificationProvider mProvider;
/**
- * Create a {@link MediaNotification}.
+ * Default constructor
*
+ * @param context context
* @param notificationId notification id to be used for
* {@link android.app.NotificationManager#notify(int, Notification)}.
* @param notification a notification to make session service foreground service. Media
* style notification is recommended here.
- * @return
*/
- public static MediaNotification create(int notificationId,
- @NonNull Notification notification) {
- if (notification == null) {
- throw new IllegalArgumentException("Notification cannot be null");
- }
- return new MediaNotification(notificationId, notification);
+ public MediaNotification(@NonNull Context context,
+ int notificationId, @NonNull Notification notification) {
+ mProvider = ApiLoader.getProvider(context)
+ .createMediaSessionService2MediaNotification(
+ context, this, notificationId, notification);
+ }
+
+ public int getNotificationId() {
+ return mProvider.getNotificationId_impl();
+ }
+
+ public Notification getNotification() {
+ return mProvider.getNotification_impl();
}
}
}
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 21f9171..131e37b 100644
--- a/media/java/android/media/MicrophoneInfo.java
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -55,7 +55,7 @@
/**
* Unknown microphone directionality.
*/
- public static final int DIRECTIONALITY_UNKNOW = 0;
+ public static final int DIRECTIONALITY_UNKNOWN = 0;
/**
* Microphone directionality type: omni.
@@ -104,7 +104,7 @@
/** @hide */
@IntDef(flag = true, prefix = { "DIRECTIONALITY_" }, value = {
- DIRECTIONALITY_UNKNOW,
+ DIRECTIONALITY_UNKNOWN,
DIRECTIONALITY_OMNI,
DIRECTIONALITY_BI_DIRECTIONAL,
DIRECTIONALITY_CARDIOID,
@@ -309,7 +309,7 @@
/**
* Returns the directionality of microphone. The return value is one of
- * {@link #DIRECTIONALITY_UNKNOW}, {@link #DIRECTIONALITY_OMNI},
+ * {@link #DIRECTIONALITY_UNKNOWN}, {@link #DIRECTIONALITY_OMNI},
* {@link #DIRECTIONALITY_BI_DIRECTIONAL}, {@link #DIRECTIONALITY_CARDIOID},
* {@link #DIRECTIONALITY_HYPER_CARDIOID}, or {@link #DIRECTIONALITY_SUPER_CARDIOID}.
*
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 3eb9d52..fefa1ed 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -28,11 +28,13 @@
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -47,22 +49,17 @@
import com.android.internal.database.SortCursor;
-import libcore.io.Streams;
-
import java.io.Closeable;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
-import static android.content.ContentProvider.maybeAddUserId;
-import static android.content.pm.PackageManager.NameNotFoundException;
-
/**
* RingtoneManager provides access to ringtones, notification, and other types
* of sounds. It manages querying the different media providers and combines the
@@ -855,7 +852,7 @@
final Uri cacheUri = getCacheForType(type, context.getUserId());
try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = resolver.openOutputStream(cacheUri)) {
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
}
@@ -960,7 +957,7 @@
// Copy contents to external ringtone storage. Throws IOException if the copy fails.
try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri);
final OutputStream output = new FileOutputStream(outFile)) {
- Streams.copy(input, output);
+ FileUtils.copy(input, output);
}
// Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
diff --git a/media/java/android/media/VolumePolicy.java b/media/java/android/media/VolumePolicy.java
index bbcce82..bd6667f 100644
--- a/media/java/android/media/VolumePolicy.java
+++ b/media/java/android/media/VolumePolicy.java
@@ -23,7 +23,7 @@
/** @hide */
public final class VolumePolicy implements Parcelable {
- public static final VolumePolicy DEFAULT = new VolumePolicy(false, false, true, 400);
+ public static final VolumePolicy DEFAULT = new VolumePolicy(false, false, false, 400);
/**
* Accessibility volume policy where the STREAM_MUSIC volume (i.e. media volume) affects
diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java
index 00746e2..53ba466 100644
--- a/media/java/android/media/VolumeProvider2.java
+++ b/media/java/android/media/VolumeProvider2.java
@@ -32,7 +32,7 @@
* {@link #setCurrentVolume(int)} each time the volume being provided changes.
* <p>
* You can set a volume provider on a session by calling
- * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider)}.
+ * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider2)}.
*
* @hide
*/
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 0fe7246..f2b4fe0 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -546,22 +546,39 @@
/**
* Method called when a new waveform capture is available.
* <p>Data in the waveform buffer is valid only within the scope of the callback.
- * Applications which needs access to the waveform data after returning from the callback
+ * Applications which need access to the waveform data after returning from the callback
* should make a copy of the data instead of holding a reference.
* @param visualizer Visualizer object on which the listener is registered.
* @param waveform array of bytes containing the waveform representation.
- * @param samplingRate sampling rate of the audio visualized.
+ * @param samplingRate sampling rate of the visualized audio.
*/
void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
/**
* Method called when a new frequency capture is available.
* <p>Data in the fft buffer is valid only within the scope of the callback.
- * Applications which needs access to the fft data after returning from the callback
+ * Applications which need access to the fft data after returning from the callback
* should make a copy of the data instead of holding a reference.
+ *
+ * <p>In order to obtain magnitude and phase values the following formulas can
+ * be used:
+ * <pre class="prettyprint">
+ * for (int i = 0; i < fft.size(); i += 2) {
+ * float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
+ * float phase = (float)Math.atan2(fft[i + 1], fft[i]);
+ * }</pre>
* @param visualizer Visualizer object on which the listener is registered.
* @param fft array of bytes containing the frequency representation.
- * @param samplingRate sampling rate of the audio visualized.
+ * The fft array only contains the first half of the actual
+ * FFT spectrum (frequencies up to Nyquist frequency), exploiting
+ * the symmetry of the spectrum. For each frequencies bin <code>i</code>:
+ * <ul>
+ * <li>the element at index <code>2*i</code> in the array contains
+ * the real part of a complex number,</li>
+ * <li>the element at index <code>2*i+1</code> contains the imaginary
+ * part of the complex number.</li>
+ * </ul>
+ * @param samplingRate sampling rate of the visualized audio.
*/
void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 4de731a..2190635 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -463,9 +463,9 @@
* Only ever called if the {@link AudioPolicy} was built with
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
* @param afi information about the focus request and the requester
- * @param requestResult the result that was returned synchronously by the framework to the
- * application, {@link #AUDIOFOCUS_REQUEST_FAILED},or
- * {@link #AUDIOFOCUS_REQUEST_DELAYED}.
+ * @param requestResult deprecated after the addition of
+ * {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)}
+ * in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}.
*/
public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
/**
@@ -534,7 +534,7 @@
sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
if (DEBUG) {
Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
- + afi.getClientId() + "reqRes=" + requestResult);
+ + afi.getClientId() + " gen=" + afi.getGen());
}
}
diff --git a/media/java/android/media/update/FrameLayoutHelper.java b/media/java/android/media/update/FrameLayoutHelper.java
deleted file mode 100644
index 983dc70..0000000
--- a/media/java/android/media/update/FrameLayoutHelper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.media.update;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-/**
- * Helper class for connecting the public API to an updatable implementation.
- *
- * @see ViewProvider
- *
- * @hide
- */
-public abstract class FrameLayoutHelper<T extends ViewProvider> extends FrameLayout {
- /** @hide */
- final public T mProvider;
-
- /** @hide */
- public FrameLayoutHelper(ProviderCreator<T> creator,
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- mProvider = creator.createProvider(this, new SuperProvider());
- }
-
- /** @hide */
- // TODO @SystemApi
- public T getProvider() {
- return mProvider;
- }
-
- @Override
- public CharSequence getAccessibilityClassName() {
- return mProvider.getAccessibilityClassName_impl();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mProvider.onTouchEvent_impl(ev);
- }
-
- @Override
- public boolean onTrackballEvent(MotionEvent ev) {
- return mProvider.onTrackballEvent_impl(ev);
- }
-
- @Override
- public void onFinishInflate() {
- mProvider.onFinishInflate_impl();
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mProvider.setEnabled_impl(enabled);
- }
-
- @Override
- protected void onAttachedToWindow() {
- mProvider.onAttachedToWindow_impl();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- mProvider.onDetachedFromWindow_impl();
- }
-
- /** @hide */
- public class SuperProvider implements ViewProvider {
- @Override
- public CharSequence getAccessibilityClassName_impl() {
- return FrameLayoutHelper.super.getAccessibilityClassName();
- }
-
- @Override
- public boolean onTouchEvent_impl(MotionEvent ev) {
- return FrameLayoutHelper.super.onTouchEvent(ev);
- }
-
- @Override
- public boolean onTrackballEvent_impl(MotionEvent ev) {
- return FrameLayoutHelper.super.onTrackballEvent(ev);
- }
-
- @Override
- public void onFinishInflate_impl() {
- FrameLayoutHelper.super.onFinishInflate();
- }
-
- @Override
- public void setEnabled_impl(boolean enabled) {
- FrameLayoutHelper.super.setEnabled(enabled);
- }
-
- @Override
- public void onAttachedToWindow_impl() {
- FrameLayoutHelper.super.onAttachedToWindow();
- }
-
- @Override
- public void onDetachedFromWindow_impl() {
- FrameLayoutHelper.super.onDetachedFromWindow();
- }
- }
-
- /** @hide */
- @FunctionalInterface
- public interface ProviderCreator<U extends ViewProvider> {
- U createProvider(FrameLayoutHelper<U> instance, ViewProvider superProvider);
- }
-}
diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java
index 67680c7..17256a8 100644
--- a/media/java/android/media/update/MediaBrowser2Provider.java
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -23,7 +23,7 @@
* @hide
*/
public interface MediaBrowser2Provider extends MediaController2Provider {
- void getBrowserRoot_impl(Bundle rootHints);
+ void getLibraryRoot_impl(Bundle rootHints);
void subscribe_impl(String parentId, Bundle options);
void unsubscribe_impl(String parentId, Bundle options);
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 95fe363..ebde3fef 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.media.session.MediaController;
+import android.util.AttributeSet;
import android.view.View;
/**
@@ -34,12 +35,10 @@
* @hide
*/
// TODO @SystemApi
-public interface MediaControlView2Provider extends ViewProvider {
+public interface MediaControlView2Provider extends ViewGroupProvider {
+ void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
void setController_impl(MediaController controller);
- boolean isShowing_impl();
void setButtonVisibility_impl(int button, int visibility);
void requestPlayButtonFocus_impl();
- void onVisibilityAggregated_impl(boolean isVisible);
- void setTimeout_impl(long timeout);
- long getTimeout_impl();
}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 05790c1..c492d307 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.media.AudioAttributes;
import android.media.MediaController2.PlaybackInfo;
import android.media.MediaItem2;
import android.media.MediaSession2.Command;
@@ -52,7 +53,7 @@
void prepareFromSearch_impl(String query, Bundle extras);
void prepareMediaId_impl(String mediaId, Bundle extras);
void playFromSearch_impl(String query, Bundle extras);
- void playFromUri_impl(String uri, Bundle extras);
+ void playFromUri_impl(Uri uri, Bundle extras);
void playFromMediaId_impl(String mediaId, Bundle extras);
void setRating_impl(Rating2 rating);
@@ -65,4 +66,12 @@
PlaylistParams getPlaylistParams_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaybackState2 getPlaybackState_impl();
+
+ interface PlaybackInfoProvider {
+ int getPlaybackType_impl();
+ AudioAttributes getAudioAttributes_impl();
+ int getControlType_impl();
+ int getMaxVolume_impl();
+ int getCurrentVolume_impl();
+ }
}
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index 87f509a..923551a 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -33,4 +33,9 @@
void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle options);
void notifyChildrenChanged_impl(String parentId, Bundle options);
}
+
+ interface LibraryRootProvider {
+ String getRootId_impl();
+ Bundle getExtras_impl();
+ }
}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index da4d0c7..41162e0 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -24,12 +24,13 @@
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.SessionToken2;
-import android.media.VolumeProvider;
+import android.media.VolumeProvider2;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -43,7 +44,7 @@
public interface MediaSession2Provider extends TransportControlProvider {
void close_impl();
void setPlayer_impl(MediaPlayerInterface player);
- void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider);
+ void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider);
MediaPlayerInterface getPlayer_impl();
SessionToken2 getToken_impl();
List<ControllerInfo> getConnectedControllers_impl();
@@ -82,6 +83,23 @@
Bundle toBundle_impl();
}
+ interface CommandButtonProvider {
+ Command getCommand_impl();
+ int getIconResId_impl();
+ String getDisplayName_impl();
+ Bundle getExtra_impl();
+ boolean isEnabled_impl();
+
+ interface BuilderProvider {
+ Builder setCommand_impl(Command command);
+ Builder setIconResId_impl(int resId);
+ Builder setDisplayName_impl(String displayName);
+ Builder setEnabled_impl(boolean enabled);
+ Builder setExtra_impl(Bundle extra);
+ CommandButton build_impl();
+ }
+ }
+
interface ControllerInfoProvider {
String getPackageName_impl();
int getUid_impl();
@@ -98,7 +116,7 @@
}
interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
- void setVolumeProvider_impl(VolumeProvider volumeProvider);
+ void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
void setRatingType_impl(int type);
void setSessionActivity_impl(PendingIntent pi);
void setId_impl(String id);
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 9455da7..42e7587 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -17,6 +17,7 @@
package android.media.update;
import android.annotation.SystemApi;
+import android.app.Notification;
import android.content.Intent;
import android.media.MediaSession2;
import android.media.MediaSessionService2.MediaNotification;
@@ -33,4 +34,9 @@
// Service
void onCreate_impl();
IBinder onBind_impl(Intent intent);
+
+ interface MediaNotificationProvider {
+ int getNotificationId_impl();
+ Notification getNotification_impl();
+ }
}
diff --git a/media/java/android/media/update/PlaybackInfoProvider.java b/media/java/android/media/update/PlaybackInfoProvider.java
deleted file mode 100644
index 36eb58a..0000000
--- a/media/java/android/media/update/PlaybackInfoProvider.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.media.update;
-
-import android.media.AudioAttributes;
-
-/**
- * @hide
- */
-// TODO(jaewan): @SystemApi
-public interface PlaybackInfoProvider {
- int getPlaybackType_impl();
- AudioAttributes getAudioAttributes_impl();
- int getControlType_impl();
- int getMaxVolume_impl();
- int getCurrentVolume_impl();
-}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 922b452..57f04cc 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,6 +17,7 @@
package android.media.update;
import android.annotation.Nullable;
+import android.app.Notification;
import android.content.Context;
import android.media.DataSourceDesc;
import android.media.MediaBrowser2;
@@ -25,25 +26,31 @@
import android.media.MediaController2.ControllerCallback;
import android.media.MediaItem2;
import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
+import android.media.MediaSession2.CommandButton.Builder;
import android.media.MediaSession2.PlaylistParams;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
import android.media.PlaybackState2;
import android.media.Rating2;
import android.media.SessionPlayer2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
import android.media.update.MediaSession2Provider.CommandGroupProvider;
import android.media.update.MediaSession2Provider.CommandProvider;
import android.media.update.MediaSession2Provider.ControllerInfoProvider;
import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.os.Bundle;
import android.os.IInterface;
import android.util.AttributeSet;
@@ -60,10 +67,11 @@
* @hide
*/
public interface StaticProvider {
- MediaControlView2Provider createMediaControlView2(
- MediaControlView2 instance, ViewProvider superProvider);
- VideoView2Provider createVideoView2(
- VideoView2 instance, ViewProvider superProvider,
+ MediaControlView2Provider createMediaControlView2(MediaControlView2 instance,
+ ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
+ @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+ VideoView2Provider createVideoView2(VideoView2 instance,
+ ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
CommandProvider createMediaSession2Command(MediaSession2.Command instance,
@@ -79,6 +87,7 @@
PlaylistParams playlistParams, int repeatMode, int shuffleMode,
MediaMetadata2 playlistMetadata);
PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
+ BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder);
BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
@@ -89,12 +98,16 @@
SessionToken2 token, Executor executor, BrowserCallback callback);
MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
+ MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+ MediaNotification mediaNotification, int notificationId, Notification notification);
MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
createMediaLibraryService2Builder(
Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
Executor callbackExecutor, MediaLibrarySessionCallback callback);
+ LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
+ String rootId, Bundle extras);
SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
String packageName, String serviceName, int uid);
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 10f03d2..7f9ecdd 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -16,12 +16,14 @@
package android.media.update;
+import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.MediaPlayerInterface;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
import android.net.Uri;
+import android.util.AttributeSet;
import android.widget.MediaControlView2;
import android.widget.VideoView2;
@@ -43,15 +45,21 @@
* @hide
*/
// TODO @SystemApi
-public interface VideoView2Provider extends ViewProvider {
- void setMediaControlView2_impl(MediaControlView2 mediaControlView);
+public interface VideoView2Provider extends ViewGroupProvider {
+ void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
+ void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs);
MediaController getMediaController_impl();
MediaControlView2 getMediaControlView2_impl();
- void showSubtitle_impl(boolean show);
+ void setSubtitleEnabled_impl(boolean enable);
+ boolean isSubtitleEnabled_impl();
// TODO: remove setSpeed_impl once MediaController2 is ready.
void setSpeed_impl(float speed);
void setAudioFocusRequest_impl(int focusGain);
void setAudioAttributes_impl(AudioAttributes attributes);
+ /**
+ * @hide
+ */
void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player);
// TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready.
void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer);
diff --git a/media/java/android/media/update/ViewGroupHelper.java b/media/java/android/media/update/ViewGroupHelper.java
new file mode 100644
index 0000000..6b4f15d
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupHelper.java
@@ -0,0 +1,369 @@
+/*
+ * 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.media.update;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper class for connecting the public API to an updatable implementation.
+ *
+ * @see ViewGroupProvider
+ *
+ * @hide
+ */
+public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup {
+ /** @hide */
+ final public T mProvider;
+
+ /** @hide */
+ public ViewGroupHelper(ProviderCreator<T> creator,
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mProvider = creator.createProvider(this, new SuperProvider(),
+ new PrivateProvider());
+ }
+
+ /** @hide */
+ // TODO @SystemApi
+ public T getProvider() {
+ return mProvider;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ mProvider.onAttachedToWindow_impl();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mProvider.onDetachedFromWindow_impl();
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return mProvider.getAccessibilityClassName_impl();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mProvider.onTouchEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ return mProvider.onTrackballEvent_impl(ev);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ mProvider.onFinishInflate_impl();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mProvider.setEnabled_impl(enabled);
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ mProvider.onVisibilityAggregated_impl(isVisible);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mProvider.onLayout_impl(changed, left, top, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected int getSuggestedMinimumWidth() {
+ return mProvider.getSuggestedMinimumWidth_impl();
+ }
+
+ @Override
+ protected int getSuggestedMinimumHeight() {
+ return mProvider.getSuggestedMinimumHeight_impl();
+ }
+
+ // setMeasuredDimension is final
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ return mProvider.dispatchTouchEvent_impl(ev);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(LayoutParams p) {
+ return mProvider.checkLayoutParams_impl(p);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return mProvider.generateDefaultLayoutParams_impl();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return mProvider.generateLayoutParams_impl(attrs);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(LayoutParams lp) {
+ return mProvider.generateLayoutParams_impl(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return mProvider.shouldDelayChildPressedState_impl();
+ }
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ mProvider.measureChildWithMargins_impl(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+
+ /** @hide */
+ public class SuperProvider implements ViewGroupProvider {
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return ViewGroupHelper.super.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.onTrackballEvent(ev);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ ViewGroupHelper.super.onFinishInflate();
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ ViewGroupHelper.super.setEnabled(enabled);
+ }
+
+ @Override
+ public void onAttachedToWindow_impl() {
+ ViewGroupHelper.super.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ ViewGroupHelper.super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ ViewGroupHelper.super.onVisibilityAggregated(isVisible);
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ // abstract method; no super
+ }
+
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ ViewGroupHelper.super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public int getSuggestedMinimumWidth_impl() {
+ return ViewGroupHelper.super.getSuggestedMinimumWidth();
+ }
+
+ @Override
+ public int getSuggestedMinimumHeight_impl() {
+ return ViewGroupHelper.super.getSuggestedMinimumHeight();
+ }
+
+ @Override
+ public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+ ViewGroupHelper.super.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean checkLayoutParams_impl(LayoutParams p) {
+ return ViewGroupHelper.super.checkLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateDefaultLayoutParams_impl() {
+ return ViewGroupHelper.super.generateDefaultLayoutParams();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+ return ViewGroupHelper.super.generateLayoutParams(attrs);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+ return ViewGroupHelper.super.generateLayoutParams(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState_impl() {
+ return ViewGroupHelper.super.shouldDelayChildPressedState();
+ }
+
+ @Override
+ public void measureChildWithMargins_impl(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ ViewGroupHelper.super.measureChildWithMargins(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+ }
+
+ /** @hide */
+ public class PrivateProvider implements ViewGroupProvider {
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return ViewGroupHelper.this.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.onTrackballEvent(ev);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ ViewGroupHelper.this.onFinishInflate();
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ ViewGroupHelper.this.setEnabled(enabled);
+ }
+
+ @Override
+ public void onAttachedToWindow_impl() {
+ ViewGroupHelper.this.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ ViewGroupHelper.this.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityAggregated_impl(boolean isVisible) {
+ ViewGroupHelper.this.onVisibilityAggregated(isVisible);
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ ViewGroupHelper.this.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+ ViewGroupHelper.this.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public int getSuggestedMinimumWidth_impl() {
+ return ViewGroupHelper.this.getSuggestedMinimumWidth();
+ }
+
+ @Override
+ public int getSuggestedMinimumHeight_impl() {
+ return ViewGroupHelper.this.getSuggestedMinimumHeight();
+ }
+
+ @Override
+ public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+ ViewGroupHelper.this.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent_impl(MotionEvent ev) {
+ return ViewGroupHelper.this.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean checkLayoutParams_impl(LayoutParams p) {
+ return ViewGroupHelper.this.checkLayoutParams(p);
+ }
+
+ @Override
+ public LayoutParams generateDefaultLayoutParams_impl() {
+ return ViewGroupHelper.this.generateDefaultLayoutParams();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+ return ViewGroupHelper.this.generateLayoutParams(attrs);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+ return ViewGroupHelper.this.generateLayoutParams(lp);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState_impl() {
+ return ViewGroupHelper.this.shouldDelayChildPressedState();
+ }
+
+ @Override
+ public void measureChildWithMargins_impl(View child,
+ int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ ViewGroupHelper.this.measureChildWithMargins(child,
+ parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+ }
+ }
+
+ /** @hide */
+ @FunctionalInterface
+ public interface ProviderCreator<T extends ViewGroupProvider> {
+ T createProvider(ViewGroupHelper<T> instance, ViewGroupProvider superProvider,
+ ViewGroupProvider privateProvider);
+ }
+}
diff --git a/media/java/android/media/update/ViewGroupProvider.java b/media/java/android/media/update/ViewGroupProvider.java
new file mode 100644
index 0000000..67e8cea
--- /dev/null
+++ b/media/java/android/media/update/ViewGroupProvider.java
@@ -0,0 +1,67 @@
+/*
+ * 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.media.update;
+
+import android.annotation.SystemApi;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.view.View
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface ViewGroupProvider {
+ // View methods
+ void onAttachedToWindow_impl();
+ void onDetachedFromWindow_impl();
+ CharSequence getAccessibilityClassName_impl();
+ boolean onTouchEvent_impl(MotionEvent ev);
+ boolean onTrackballEvent_impl(MotionEvent ev);
+ void onFinishInflate_impl();
+ void setEnabled_impl(boolean enabled);
+ void onVisibilityAggregated_impl(boolean isVisible);
+ void onLayout_impl(boolean changed, int left, int top, int right, int bottom);
+ void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec);
+ int getSuggestedMinimumWidth_impl();
+ int getSuggestedMinimumHeight_impl();
+ void setMeasuredDimension_impl(int measuredWidth, int measuredHeight);
+ boolean dispatchTouchEvent_impl(MotionEvent ev);
+
+ // ViewGroup methods
+ boolean checkLayoutParams_impl(LayoutParams p);
+ LayoutParams generateDefaultLayoutParams_impl();
+ LayoutParams generateLayoutParams_impl(AttributeSet attrs);
+ LayoutParams generateLayoutParams_impl(LayoutParams lp);
+ boolean shouldDelayChildPressedState_impl();
+ void measureChildWithMargins_impl(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed);
+
+ // ViewManager methods
+ // ViewParent methods
+}
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
deleted file mode 100644
index 0dd8f38..0000000
--- a/media/java/android/media/update/ViewProvider.java
+++ /dev/null
@@ -1,45 +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.media.update;
-
-import android.annotation.SystemApi;
-import android.view.MotionEvent;
-
-/**
- * Interface for connecting the public API to an updatable implementation.
- *
- * Each instance object is connected to one corresponding updatable object which implements the
- * runtime behavior of that class. There should a corresponding provider method for all public
- * methods.
- *
- * All methods behave as per their namesake in the public API.
- *
- * @see android.view.View
- *
- * @hide
- */
-// TODO @SystemApi
-public interface ViewProvider {
- // TODO Add more (all?) methods from View
- void onAttachedToWindow_impl();
- void onDetachedFromWindow_impl();
- CharSequence getAccessibilityClassName_impl();
- boolean onTouchEvent_impl(MotionEvent ev);
- boolean onTrackballEvent_impl(MotionEvent ev);
- void onFinishInflate_impl();
- void setEnabled_impl(boolean enabled);
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7e5f581..fe2f64f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -127,11 +127,11 @@
"liblzma",
"libmedia",
"libmedia_helper",
- "libmedia_player2",
"libmedia_player2_util",
"libmediadrm",
"libmediaextractor",
"libmediametrics",
+ "libmediaplayer2",
"libmediautils",
"libnativehelper",
"libnetd_client",
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
new file mode 100644
index 0000000..71b8dac
--- /dev/null
+++ b/media/jni/android_media_AudioPresentation.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+
+#include "jni.h"
+
+#include <media/AudioPresentationInfo.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+struct JAudioPresentationInfo {
+ struct fields_t {
+ jclass clazz;
+ jmethodID constructID;
+
+ // list parameters
+ jclass listclazz;
+ jmethodID listConstructId;
+ jmethodID listAddId;
+
+ void init(JNIEnv *env) {
+ jclass lclazz = env->FindClass("android/media/AudioPresentation");
+ if (lclazz == NULL) {
+ return;
+ }
+
+ clazz = (jclass)env->NewGlobalRef(lclazz);
+ if (clazz == NULL) {
+ return;
+ }
+
+ constructID = env->GetMethodID(clazz, "<init>",
+ "(IILjava/util/Map;Ljava/lang/String;IZZZ)V");
+ env->DeleteLocalRef(lclazz);
+
+ // list objects
+ jclass llistclazz = env->FindClass("java/util/ArrayList");
+ CHECK(llistclazz != NULL);
+ listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
+ CHECK(listclazz != NULL);
+ listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+ CHECK(listConstructId != NULL);
+ listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+ CHECK(listAddId != NULL);
+ env->DeleteLocalRef(llistclazz);
+ }
+
+ void exit(JNIEnv *env) {
+ env->DeleteGlobalRef(clazz);
+ clazz = NULL;
+ env->DeleteGlobalRef(listclazz);
+ listclazz = NULL;
+ }
+ };
+
+ static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+ ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
+
+ if (hashMapClazz.get() == NULL) {
+ return -EINVAL;
+ }
+ jmethodID hashMapConstructID =
+ env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+ if (hashMapConstructID == NULL) {
+ return -EINVAL;
+ }
+ jmethodID hashMapPutID =
+ env->GetMethodID(
+ hashMapClazz.get(),
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ if (hashMapPutID == NULL) {
+ return -EINVAL;
+ }
+
+ jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+ for (size_t i = 0; i < msg->countEntries(); ++i) {
+ AMessage::Type valueType;
+ const char *key = msg->getEntryNameAt(i, &valueType);
+
+ if (!strncmp(key, "android._", 9)) {
+ // don't expose private keys (starting with android._)
+ continue;
+ }
+
+ jobject valueObj = NULL;
+
+ AString val;
+ CHECK(msg->findString(key, &val));
+
+ valueObj = env->NewStringUTF(val.c_str());
+
+ if (valueObj != NULL) {
+ jstring keyObj = env->NewStringUTF(key);
+
+ env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+ env->DeleteLocalRef(keyObj); keyObj = NULL;
+ env->DeleteLocalRef(valueObj); valueObj = NULL;
+ }
+ }
+
+ *map = hashMap;
+
+ return OK;
+ }
+
+ jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
+ jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
+
+ for (size_t i = 0; i < info.countPresentations(); ++i) {
+ const sp<AudioPresentation> &ap = info.getPresentation(i);
+ jobject jLabelObject;
+
+ sp<AMessage> labelMessage = new AMessage();
+ for (size_t i = 0; i < ap->mLabels.size(); ++i) {
+ labelMessage->setString(ap->mLabels.keyAt(i).string(),
+ ap->mLabels.valueAt(i).string());
+ }
+ if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
+ return NULL;
+ }
+ jstring jLanguage = env->NewStringUTF(ap->mLanguage.string());
+
+ jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
+ static_cast<jint>(ap->mPresentationId),
+ static_cast<jint>(ap->mProgramId),
+ jLabelObject,
+ jLanguage,
+ static_cast<jint>(ap->mMasteringIndication),
+ static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
+ 1 : 0),
+ static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
+ 1 : 0),
+ static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
+ 1 : 0));
+ if (jValueObj == NULL) {
+ env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+ return NULL;
+ }
+
+ env->CallBooleanMethod(list, fields.listAddId, jValueObj);
+ env->DeleteLocalRef(jValueObj); jValueObj = NULL;
+ env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+ }
+ return list;
+ }
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 90ee8a6..0eb98f3 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -19,13 +19,16 @@
#define LOG_TAG "MediaPlayer2-JNI"
#include "utils/Log.h"
-#include <media/mediaplayer2.h>
+#include <sys/stat.h>
+
#include <media/AudioResamplerPublic.h>
+#include <media/DataSourceDesc.h>
#include <media/MediaHTTPService.h>
-#include <media/MediaPlayer2Interface.h>
#include <media/MediaAnalyticsItem.h>
#include <media/NdkWrapper.h>
+#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
+#include <mediaplayer2/mediaplayer2.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
@@ -234,7 +237,8 @@
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
-static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
+static void process_media_player_call(
+ JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
@@ -268,7 +272,7 @@
jobjectArray keys, jobjectArray values) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
+ if (mp == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
@@ -282,16 +286,25 @@
if (tmp == NULL) { // Out of memory
return;
}
- ALOGV("setDataSource: path %s", tmp);
+ ALOGV("setDataSourceAndHeaders: path %s", tmp);
- String8 pathStr(tmp);
+ if (strncmp(tmp, "content://", 10) == 0) {
+ ALOGE("setDataSourceAndHeaders: content scheme is not supported in native code");
+ jniThrowException(env, "java/io/IOException",
+ "content scheme is not supported in native code");
+ return;
+ }
+
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_URL;
+ dsd->mUrl = tmp;
+
env->ReleaseStringUTFChars(path, tmp);
tmp = NULL;
// We build a KeyedVector out of the key and val arrays
- KeyedVector<String8, String8> headersVector;
if (!ConvertKeyValueArraysToKeyedVector(
- env, keys, values, &headersVector)) {
+ env, keys, values, &dsd->mHeaders)) {
return;
}
@@ -299,20 +312,16 @@
if (httpServiceObj != NULL) {
httpService = new JMedia2HTTPService(env, httpServiceObj);
}
-
- status_t opStatus =
- mp->setDataSource(
- httpService,
- pathStr,
- headersVector.size() > 0? &headersVector : NULL);
+ dsd->mHttpService = httpService;
process_media_player_call(
- env, thiz, opStatus, "java/io/IOException",
- "setDataSource failed." );
+ env, thiz, mp->setDataSource(dsd), "java/io/IOException",
+ "setDataSourceAndHeaders failed." );
}
static void
-android_media_MediaPlayer2_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
+android_media_MediaPlayer2_setDataSourceFD(
+ JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -325,12 +334,46 @@
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ALOGV("setDataSourceFD: fd %d", fd);
- process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
+ ALOGV("setDataSourceFD: fd=%d (%s), offset=%lld, length=%lld",
+ fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
+
+ struct stat sb;
+ int ret = fstat(fd, &sb);
+ if (ret != 0) {
+ ALOGE("setDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
+ jniThrowException(env, "java/io/IOException", "setDataSourceFD failed fstat");
+ return;
+ }
+
+ ALOGV("st_dev = %llu", static_cast<unsigned long long>(sb.st_dev));
+ ALOGV("st_mode = %u", sb.st_mode);
+ ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
+ ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
+ ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));
+
+ if (offset >= sb.st_size) {
+ ALOGE("setDataSourceFD: offset is out of range");
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "setDataSourceFD failed, offset is out of range.");
+ return;
+ }
+ if (offset + length > sb.st_size) {
+ length = sb.st_size - offset;
+ ALOGV("setDataSourceFD: adjusted length = %lld", (long long)length);
+ }
+
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_FD;
+ dsd->mFD = fd;
+ dsd->mFDOffset = offset;
+ dsd->mFDLength = length;
+ process_media_player_call(env, thiz, mp->setDataSource(dsd),
+ "java/io/IOException", "setDataSourceFD failed." );
}
static void
-android_media_MediaPlayer2_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
+android_media_MediaPlayer2_setDataSourceCallback(
+ JNIEnv *env, jobject thiz, jobject dataSource)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -343,7 +386,11 @@
return;
}
sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
- process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." );
+ sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mType = DataSourceDesc::TYPE_CALLBACK;
+ dsd->mCallbackSource = callbackDataSource;
+ process_media_player_call(env, thiz, mp->setDataSource(dsd),
+ "java/lang/RuntimeException", "setDataSourceCallback failed." );
}
static sp<ANativeWindowWrapper>
@@ -1099,45 +1146,6 @@
process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
}
-static jint
-android_media_MediaPlayer2_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
- jstring addrString, jint port) {
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return INVALID_OPERATION;
- }
-
- const char *cAddrString = NULL;
-
- if (NULL != addrString) {
- cAddrString = env->GetStringUTFChars(addrString, NULL);
- if (cAddrString == NULL) { // Out of memory
- return NO_MEMORY;
- }
- }
- ALOGV("setRetransmitEndpoint: %s:%d",
- cAddrString ? cAddrString : "(null)", port);
-
- status_t ret;
- if (cAddrString && (port > 0xFFFF)) {
- ret = BAD_VALUE;
- } else {
- ret = mp->setRetransmitEndpoint(cAddrString,
- static_cast<uint16_t>(port));
- }
-
- if (NULL != addrString) {
- env->ReleaseStringUTFChars(addrString, cAddrString);
- }
-
- if (ret == INVALID_OPERATION ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- }
-
- return (jint) ret;
-}
-
static void
android_media_MediaPlayer2_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
{
@@ -1418,7 +1426,6 @@
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
- {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer2_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer2;)V", (void *)android_media_MediaPlayer2_setNextMediaPlayer},
// Modular DRM
{ "_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 00fe638..4fb5e74 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -48,6 +48,7 @@
"sensor.cpp",
"sharedmem.cpp",
"storage_manager.cpp",
+ "surface_texture.cpp",
"trace.cpp",
],
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 98e9a42..e70d5ea 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -18,9 +18,11 @@
#include <utils/Log.h>
#include <android/asset_manager_jni.h>
+#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <utils/threads.h>
#include "jni.h"
@@ -35,21 +37,20 @@
// -----
struct AAssetDir {
- AssetDir* mAssetDir;
+ std::unique_ptr<AssetDir> mAssetDir;
size_t mCurFileIndex;
String8 mCachedFileName;
- explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { }
- ~AAssetDir() { delete mAssetDir; }
+ explicit AAssetDir(std::unique_ptr<AssetDir> dir) :
+ mAssetDir(std::move(dir)), mCurFileIndex(0) { }
};
// -----
struct AAsset {
- Asset* mAsset;
+ std::unique_ptr<Asset> mAsset;
- explicit AAsset(Asset* asset) : mAsset(asset) { }
- ~AAsset() { delete mAsset; }
+ explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { }
};
// -------------------- Public native C API --------------------
@@ -104,19 +105,18 @@
return NULL;
}
- AssetManager* mgr = static_cast<AssetManager*>(amgr);
- Asset* asset = mgr->open(filename, amMode);
- if (asset == NULL) {
- return NULL;
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
+ std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode);
+ if (asset == nullptr) {
+ return nullptr;
}
-
- return new AAsset(asset);
+ return new AAsset(std::move(asset));
}
AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName)
{
- AssetManager* mgr = static_cast<AssetManager*>(amgr);
- return new AAssetDir(mgr->openDir(dirName));
+ ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
+ return new AAssetDir(locked_mgr->OpenDir(dirName));
}
/**
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3d5ee39..d6dcd72 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -205,6 +205,14 @@
AStorageManager_mountObb;
AStorageManager_new;
AStorageManager_unmountObb;
+ ASurfaceTexture_acquireANativeWindow; # introduced=28
+ ASurfaceTexture_attachToGLContext; # introduced=28
+ ASurfaceTexture_detachFromGLContext; # introduced=28
+ ASurfaceTexture_fromSurfaceTexture; # introduced=28
+ ASurfaceTexture_getTimestamp; # introduced=28
+ ASurfaceTexture_getTransformMatrix; # introduced=28
+ ASurfaceTexture_release; # introduced=28
+ ASurfaceTexture_updateTexImage; # introduced=28
ATrace_beginSection; # introduced=23
ATrace_endSection; # introduced=23
ATrace_isEnabled; # introduced=23
diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp
new file mode 100644
index 0000000..b266881
--- /dev/null
+++ b/native/android/surface_texture.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.
+ */
+
+#include <android/surface_texture.h>
+#include <android/surface_texture_jni.h>
+
+#define LOG_TAG "ASurfaceTexture"
+
+#include <utils/Log.h>
+
+#include <gui/GLConsumer.h>
+#include <gui/Surface.h>
+
+#include <android_runtime/android_graphics_SurfaceTexture.h>
+
+using namespace android;
+
+struct ASurfaceTexture {
+ sp<GLConsumer> consumer;
+ sp<IGraphicBufferProducer> producer;
+};
+
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) {
+ return nullptr;
+ }
+ ASurfaceTexture* ast = new ASurfaceTexture;
+ ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture);
+ ast->producer = SurfaceTexture_getProducer(env, surfacetexture);
+ return ast;
+}
+
+ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) {
+ sp<Surface> surface = new Surface(st->producer);
+ ANativeWindow* win(surface.get());
+ ANativeWindow_acquire(win);
+ return win;
+}
+
+void ASurfaceTexture_release(ASurfaceTexture* st) {
+ delete st;
+}
+
+int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) {
+ return st->consumer->attachToContext(tex);
+}
+
+int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) {
+ return st->consumer->detachFromContext();
+}
+
+int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) {
+ return st->consumer->updateTexImage();
+}
+
+void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ st->consumer->getTransformMatrix(mtx);
+}
+
+int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) {
+ return st->consumer->getTimestamp();
+}
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
index 6f77e04..ce50558 100644
--- a/packages/CaptivePortalLogin/OWNERS
+++ b/packages/CaptivePortalLogin/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml
index ad2fc24..bc1ff33 100644
--- a/packages/CarrierDefaultApp/res/values-bs/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml
@@ -7,7 +7,7 @@
<string name="no_data_notification_id" msgid="668400731803969521">"Prijenos podataka na mobilnoj mreži je deaktiviran"</string>
<string name="portal_notification_detail" msgid="2295729385924660881">"Dodirnite da posjetite %s web lokaciju"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"Obratite se pružaocu usluga %s"</string>
- <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nema mobilnog prijenosa podataka"</string>
+ <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nema veze za prijenos podataka na mobilnoj mreži"</string>
<string name="no_mobile_data_connection" msgid="544980465184147010">"Dodajte plan prijenosa podataka ili rominga putem operatera %s"</string>
<string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status prijenosa podataka na mobilnoj mreži"</string>
<string name="action_bar_label" msgid="4290345990334377177">"Prijava na mobilnu mrežu"</string>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 8b01aef..9f165bc 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
import android.content.res.ObbScanner;
import android.os.Binder;
import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -43,7 +44,6 @@
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
-import libcore.io.Streams;
import java.io.File;
import java.io.FileInputStream;
@@ -260,7 +260,7 @@
in = new FileInputStream(sourcePath);
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 6fe8975..9a66b07 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -94,7 +94,7 @@
infile = mFile.openRead();
readXml(infile);
} catch (FileNotFoundException e) {
- // No data yet
+ Log.d(TAG, "File doesn't exist or isn't readable yet");
} catch (IOException e) {
Log.e(TAG, "Unable to read channel impressions", e);
} catch (NumberFormatException | XmlPullParserException e) {
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 7c35b48..db48f61 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -325,7 +325,8 @@
int dismiss2 = 777;
String key2 = mAssistant.getKey("pkg2", 2, "channel2");
- String xml = "<assistant version=\"1\">\n"
+ String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<assistant version=\"1\">\n"
+ "<impression-set key=\"" + key1 + "\" "
+ "dismisses=\"" + dismiss1 + "\" views=\"" + views1
+ "\" streak=\"" + streak1 + "\"/>\n"
@@ -377,7 +378,6 @@
mAssistant.insertImpressions(key2, ci2);
mAssistant.insertImpressions(key3, ci3);
-
XmlSerializer serializer = new FastXmlSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index f6a259d..53e8813 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -16,8 +16,8 @@
package com.android.printspooler.model;
-import static com.android.internal.print.DumpUtils.writeComponentName;
import static com.android.internal.print.DumpUtils.writePrintJobInfo;
+import static com.android.internal.util.dump.DumpUtils.writeComponentName;
import android.annotation.FloatRange;
import android.annotation.NonNull;
@@ -59,10 +59,10 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
-import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;
@@ -214,12 +214,11 @@
try {
synchronized (mLock) {
if (dumpAsProto) {
- dumpLocked(new DualDumpOutputStream(new ProtoOutputStream(fd), null));
+ dumpLocked(new DualDumpOutputStream(new ProtoOutputStream(fd)));
} else {
try (FileOutputStream out = new FileOutputStream(fd)) {
try (PrintWriter w = new PrintWriter(out)) {
- dumpLocked(new DualDumpOutputStream(null, new IndentingPrintWriter(w,
- " ")));
+ dumpLocked(new DualDumpOutputStream(new IndentingPrintWriter(w, " ")));
}
} catch (IOException ignored) {
}
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
index 72be35b..15dd64b 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
@@ -20,6 +20,8 @@
<option name="test-suite-tag" value="apct" />
<option name="test-tag" value="PrintSpoolerOutOfProcessTests" />
+ <option name="config-descriptor:metadata" key="component" value="print" />
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.printspooler.outofprocess.tests" />
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
diff --git a/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml b/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml
index ac56a2d..bc330c7 100644
--- a/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml
+++ b/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml
@@ -22,25 +22,40 @@
android:fillViewport ="true"
android:orientation="vertical">
- <com.android.settingslib.notification.ZenRadioLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/zen_conditions"
+ <LinearLayout
+ android:id="@+id/dialog_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="4dp"
- android:layout_marginStart="4dp"
- android:paddingBottom="4dp"
- android:orientation="horizontal">
- <RadioGroup
- android:id="@+id/zen_radio_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <LinearLayout
- android:id="@+id/zen_radio_buttons_content"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"/>
- </com.android.settingslib.notification.ZenRadioLayout>
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.android.settingslib.notification.ZenRadioLayout
+ android:id="@+id/zen_conditions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginEnd="4dp"
+ android:layout_marginStart="4dp"
+ android:paddingBottom="4dp"
+ android:orientation="horizontal">
+ <RadioGroup
+ android:id="@+id/zen_radio_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <LinearLayout
+ android:id="@+id/zen_radio_buttons_content"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"/>
+ </com.android.settingslib.notification.ZenRadioLayout>
+
+ <TextView
+ android:id="@+id/zen_alarm_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="18dp"
+ android:layout_marginEnd="16dp"
+ android:textDirection="locale"
+ android:textColor="?android:attr/colorError"/>
+ </LinearLayout>
</ScrollView>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 771abe6..f995d58 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Wys oortrekareas"</item>
<item msgid="2290859360633824369">"Wys areas vir Deuteranomaly"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (verstek)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Standaardlimiet"</item>
<item msgid="4071574792028999443">"Geen agtergrondprosesse"</item>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index b3ea9fb..278b07d 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Wys hardewarelae se opdaterings"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Laat hardewarelae groen flits wanneer hulle opgedateer word"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Ontfout GPU-oortrek"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Stel GPU-leweraar"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Deaktiveer HW-oorleggers"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Gebruik altyd GPU vir skermsamestelling"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simuleer kleurruimte"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Meer tyd."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Minder tyd."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Skakel aan"</string>
+ <string name="cancel" msgid="6859253417269739139">"Kanselleer"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Skakel Moenie steur nie aan"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Nooit"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Net prioriteit"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index 3cda6ab..3f6d3b8 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Mostra les àrees sobredibuixades"</item>
<item msgid="2290859360633824369">"Mostra les àrees amb deuteranomalia"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (predeterminat)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Límit estàndard"</item>
<item msgid="4071574792028999443">"Sense processos en segon pla"</item>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 60531f2..d020f80 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Mostra actualitzacions capes"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Il·lumina capes de maquinari en verd en actualitzar-se"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Depura sobredibuix de GPU"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Configura renderitzador GPU"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Desactiva superposicions HW"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Utilitza sempre GPU per combinar pantalles"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simula l\'espai de color"</string>
@@ -354,14 +353,14 @@
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="845431008899029842">"Temps restant aproximat: <xliff:g id="TIME">^1</xliff:g>"</string>
<string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME">^1</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">^1</xliff:g> per completar la pujada"</string>
+ <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">^1</xliff:g> per completar la càrrega"</string>
<string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Temps restant: <xliff:g id="TIME">^1</xliff:g>"</string>
<string name="power_remaining_duration_only_short_enhanced" msgid="7450425624026394823">"Temps restant segons l\'ús que en fas: <xliff:g id="TIME">^1</xliff:g>"</string>
<string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">^1</xliff:g>: <xliff:g id="TIME">^2</xliff:g> aproximadament per esgotar la bateria"</string>
<string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">^1</xliff:g>; temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME">^2</xliff:g>"</string>
<string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">^1</xliff:g>; temps restant: <xliff:g id="TIME">^2</xliff:g>"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">^1</xliff:g>: <xliff:g id="TIME">^2</xliff:g> per completar la pujada"</string>
+ <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">^1</xliff:g>: <xliff:g id="TIME">^2</xliff:g> per completar la càrrega"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"s\'està carregant"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Més temps"</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Menys temps"</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Activa"</string>
+ <string name="cancel" msgid="6859253417269739139">"Cancel·la"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Activa el mode No molestis"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Mai"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Només amb prioritat"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 9d665a3..7e7bbe1 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Zobrazit překreslované oblasti"</item>
<item msgid="2290859360633824369">"Zobrazit oblasti pro deuteranomálii"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (výchozí)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Standardní limit"</item>
<item msgid="4071574792028999443">"Procesy na pozadí deaktivovány"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 058f6a7..c29973c 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Zobrazit aktual. HW vrstev"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Rozblikat zeleně hardwarové vrstvy při aktualizaci"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Ladit překreslování GPU"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Nastavit modul vykreslení"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Zakázat hardwarové vrstvy"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Vždy použít GPU ke skládání obrazovky"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simulovat barevný prostor"</string>
@@ -411,4 +410,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Delší doba"</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Kratší doba"</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Zapnout"</string>
+ <string name="cancel" msgid="6859253417269739139">"Zrušit"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Zapněte funkci Nerušit"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Nikdy"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Pouze prioritní"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 0993d31..6d651d5 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Vis områder med overlap"</item>
<item msgid="2290859360633824369">"Vis områder for deuteranomali"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (standard)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Standardgrænse"</item>
<item msgid="4071574792028999443">"Ingen baggrundsprocesser"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 98bbaa2..14d07ff 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Se opdat. af hardwarelag"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Hardwarelag blinker grønt, når de opdateres"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Ret GPU-overlapsfejl"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Angiv GPU-gengiver"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Deaktiver HW-overlejring"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Brug altid GPU til skærmsammensætning"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simuler farverum"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Mere tid."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Mindre tid."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Aktivér"</string>
+ <string name="cancel" msgid="6859253417269739139">"Annuller"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Aktivér Forstyr ikke"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Aldrig"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Kun prioritet"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index 846e314..b66ace9 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Show overdraw areas"</item>
<item msgid="2290859360633824369">"Show areas for Deuteranomaly"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (Default)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Standard limit"</item>
<item msgid="4071574792028999443">"No background processes"</item>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 6c9be08..7eb1b31 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Show hardware layers updates"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Flash hardware layers green when they update"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Debug GPU overdraw"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Set GPU Renderer"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Disable HW overlays"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Always use GPU for screen compositing"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simulate color space"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"More time."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Less time."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Turn on"</string>
+ <string name="cancel" msgid="6859253417269739139">"Cancel"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Turn on Do Not Disturb"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Never"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Priority only"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index 9ed0c89..0bb28e0 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Näytä päällekkäiset alueet"</item>
<item msgid="2290859360633824369">"Näytä alueet puna-vihersokeille näkyvinä"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (oletus)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Vakioraja"</item>
<item msgid="4071574792028999443">"Ei taustaprosesseja"</item>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index be19ed6..5f9bf48 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Näytä laitt.tason päiv."</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Näytä laitteistotasot vihreinä niiden päivittyessä"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"GPU-objektien päällekkäisyys"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Grafiikan piirtäjä"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Poista HW-peittok. käyt."</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Käytä GPU:ta näytön koostamiseen"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simuloi väriavaruus"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Enemmän aikaa"</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Vähemmän aikaa"</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Ota käyttöön"</string>
+ <string name="cancel" msgid="6859253417269739139">"Peruuta"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Ota Älä häiritse ‑tila käyttöön"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Ei koskaan"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Vain tärkeät"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 13b2c4a..4b5201c 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -196,7 +196,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"નેટવર્કિંગ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"વાયરલેસ ડિસ્પ્લે પ્રમાણન"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"વાઇ-ફાઇ વર્બોઝ લૉગિંગ સક્ષમ કરો"</string>
- <string name="wifi_connected_mac_randomization" msgid="3168165236877957767">"MAC ઍડ્રેસ રેન્ડમાઇઝ કરવું ચાલુ કર્યું"</string>
+ <string name="wifi_connected_mac_randomization" msgid="3168165236877957767">"કનેક્ટ કરેલ MAC ઍડ્રેસને રેન્ડમાઇઝ કરવાનું ચાલુ કરો"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"મોબાઇલ ડેટા હંમેશાં સક્રિય"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ટિથરિંગ માટે હાર્ડવેર ગતિવૃદ્ધિ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 1a765b3..cb8d851 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"ओवरड्रॉ क्षेत्रों को दिखाएं"</item>
<item msgid="2290859360633824369">"हरा रंग पहचानने में कमी के क्षेत्र दिखाएं"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (डिफ़ॉल्ट)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"मानक सीमा"</item>
<item msgid="4071574792028999443">"कोई पृष्ठभूमि प्रक्रियाएं नहीं"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index a7a6920..16654be 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"हार्डवेयर लेयर अपडेट दिखाएं"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"हार्डवेयर लेयर अपडेट होने पर उनमें हरी रोशनी डालें"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"GPU ओवरड्रॉ डीबग करें"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"GPU दाता सेट करें"</string>
<string name="disable_overlays" msgid="2074488440505934665">"HW ओवरले बंद करें"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"स्क्रीन संयोजन के लिए हमेशा GPU का उपयोग करें"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"रंग स्पेस सिम्युलेट करें"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"ज़्यादा समय."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"कम समय."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"चालू करें"</string>
+ <string name="cancel" msgid="6859253417269739139">"रद्द करें"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'परेशान न करें\' चालू करें"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"कभी नहीं"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"सिर्फ़ ज़रूरी"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 6064949..2f21e5f 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"अधोरेखांकित क्षेत्रे दर्शवा"</item>
<item msgid="2290859360633824369">"हरित असंगतिसाठी क्षेत्रे दर्शवा"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (डीफॉल्ट)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"मानक मर्यादा"</item>
<item msgid="4071574792028999443">"एकही पार्श्वभूमी प्रक्रिया नाही"</item>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index a15869e..c1acfd0 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"हार्डवेअर स्तर अपडेट दर्शवा"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"हार्डवेअर स्तर अद्ययावत झाल्यावर ते हिरव्या रंगात फ्लॅश करा"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"GPU ओव्हरड्रॉ डीबग करा"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"GPU प्रदाता सेट करा"</string>
<string name="disable_overlays" msgid="2074488440505934665">"HW ओव्हरले अक्षम करा"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"स्क्रीन तयार करण्यासाठी नेहमी GPU वापरा"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"रंग स्थानाची बतावणी करा"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"जास्त वेळ."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"कमी वेळ."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"चालू करा"</string>
+ <string name="cancel" msgid="6859253417269739139">"रद्द करा"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"व्यत्यय आणू नका चालू करा"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"कधीही नाही"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"केवळ प्राधान्य"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index c501596..77ebdc7 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Vis maskinvarelag-oppdat."</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Maskinvarelag blinker grønt under oppdatering"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Feilsøk GPU-overtrekk"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Angi GPU-gjengivelse"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Slå av maskinvareoverlegg"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Bruk alltid GPU for skjermsammensetting"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simuler fargeområde"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Mer tid."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Mindre tid."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Slå på"</string>
+ <string name="cancel" msgid="6859253417269739139">"Avbryt"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Slå på Ikke forstyrr"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Aldri"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Bare prioritet"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index a44ac29..9aeb083 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -174,9 +174,9 @@
<string name="development_settings_enable" msgid="542530994778109538">"विकासकर्ता विकल्प सक्रिया गर्नुहोस्"</string>
<string name="development_settings_summary" msgid="1815795401632854041">"अनुप्रयोग विकासको लागि विकल्पहरू सेट गर्नुहोस्"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"विकासकर्ताका विकल्पहरू यस प्रयोगकर्ताका लागि उपलब्ध छैन"</string>
- <string name="vpn_settings_not_available" msgid="956841430176985598">"VPN सेटिङ्हरू यो प्रयोगकर्ताको लागि उपलब्ध छैन"</string>
- <string name="tethering_settings_not_available" msgid="6765770438438291012">"कार्यक्षेत्र सीमा सेटिङ्हरू यो प्रयोगकर्ताको लागि उपलब्ध छैन"</string>
- <string name="apn_settings_not_available" msgid="7873729032165324000">"पहुँच बिन्दु नामको सेटिङ्हरू यो प्रयोगकर्ताको लागि उपलब्ध छैन"</string>
+ <string name="vpn_settings_not_available" msgid="956841430176985598">"VPN सेटिङहरू यो प्रयोगकर्ताको लागि उपलब्ध छैन"</string>
+ <string name="tethering_settings_not_available" msgid="6765770438438291012">"कार्यक्षेत्र सीमा सेटिङहरू यो प्रयोगकर्ताको लागि उपलब्ध छैन"</string>
+ <string name="apn_settings_not_available" msgid="7873729032165324000">"पहुँच बिन्दु नामको सेटिङहरू यो प्रयोगकर्ताको लागि उपलब्ध छैन"</string>
<string name="enable_adb" msgid="7982306934419797485">"USB डिबग गर्दै"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"USB जडित हुँदा डिबग मोड"</string>
<string name="clear_adb_keys" msgid="4038889221503122743">"USB डिबग गर्ने प्राधिकरणहरू उल्टाउनुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index ace58fd..6fcdfd2 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Gedeeltes met overbelasting weergeven"</item>
<item msgid="2290859360633824369">"Gebieden voor deuteranomalie weergeven"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (standaard)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Standaardlimiet"</item>
<item msgid="4071574792028999443">"Geen achtergrondprocessen"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 9fe32ff..c1ea14e 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Updaten hardwarelgn wrgvn"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Hardwarelagen knipperen groen bij updates"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Foutopsporing GPU-overbelasting"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"GPU-renderer instellen"</string>
<string name="disable_overlays" msgid="2074488440505934665">"HW-overlays uitschakelen"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"GPU altijd gebruiken voor schermcompositing"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Kleurruimte simuleren"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Meer tijd."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Minder tijd."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Inschakelen"</string>
+ <string name="cancel" msgid="6859253417269739139">"Annuleren"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Schakel Niet storen in."</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Nooit"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Alleen prioriteit"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 78a24c4..384d9f5 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Mostrar áreas de sobreposição"</item>
<item msgid="2290859360633824369">"Mostrar áreas para Deuteranomaly"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (predefinição)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Limite padrão"</item>
<item msgid="4071574792028999443">"Sem processos em segundo plano"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 16e3d2b..2965ca7 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Mostrar atual. cam. hard."</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Camadas de hard. flash verdes quando estão atuali."</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Depurar sobreposição GPU"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Definir renderizador GPU"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Desativ. sobreposições HW"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Utilizar sempre GPU para a composição do ecrã"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Simular espaço da cor"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Mais tempo."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Menos tempo."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Ativar"</string>
+ <string name="cancel" msgid="6859253417269739139">"Cancelar"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Ativar o modo Não incomodar"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Nunca"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Apenas prioridade"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 05ccc80..4e771b7 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Показывать области наложения"</item>
<item msgid="2290859360633824369">"Выделять области определенного цвета"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (по умолчанию)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Стандартное ограничение"</item>
<item msgid="4071574792028999443">"Без фоновых процессов"</item>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bdf8a4a..a4732eb 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Показ. аппаратные обновл."</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Выделять аппаратные уровни зеленым при обновлении"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Отладка наложения"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Средство визуализации"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Откл. аппарат. наложения"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Всегда использовать GPU при компоновке экрана"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Имитировать аномалию"</string>
@@ -411,4 +410,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Увеличить продолжительность"</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Уменьшить продолжительность"</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Включить"</string>
+ <string name="cancel" msgid="6859253417269739139">"Отмена"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Включите режим \"Не беспокоить\""</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Никогда"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Только важные"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index e3f21ad..4a1c626 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Показувати області накладання"</item>
<item msgid="2290859360633824369">"Показувати області дейтераномалії"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (за умовчанням)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Стандартне обмеження"</item>
<item msgid="4071574792028999443">"Без фонових процесів"</item>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index e9f6c06..6b864ea 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Показ. апаратні оновлення"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Виділяти апаратні рівні зеленим під час оновлення"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Налагодити накладання GPU"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Вибрати засіб обробки відео для ГП"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Вимк. апаратн. накладання"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Завжди використовувати GPU для компонування екрана"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Моделювання кольорів"</string>
@@ -411,4 +410,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Більше часу."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Менше часу."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Увімкнути"</string>
+ <string name="cancel" msgid="6859253417269739139">"Скасувати"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Увімкнути режим \"Не турбувати\""</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Ніколи"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Лише пріоритетні"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index ce2e89fd..7e5696e 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"顯示透支區域"</item>
<item msgid="2290859360633824369">"顯示綠色弱視區域"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"OpenGL (預設)"</item>
- <item msgid="2839130076198120436">"OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"標準限制"</item>
<item msgid="4071574792028999443">"不執行背景處理程序"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 6b7d7e8..4e9e4a8 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"顯示硬體層更新"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"在硬體層更新時閃綠燈"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"針對 GPU 重複繪圖進行偵錯"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"設定 GPU 轉譯器"</string>
<string name="disable_overlays" msgid="2074488440505934665">"停用硬體重疊圖層"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"一律使用 GPU 進行畫面合成"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"模擬色彩空間"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"增加時間。"</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"減少時間。"</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"開啟"</string>
+ <string name="cancel" msgid="6859253417269739139">"取消"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"開啟「零打擾」模式"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"永不"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"僅限優先通知"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 3d0de36..6361dc1 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -234,10 +234,6 @@
<item msgid="7688197031296835369">"Bonisa izindawo zokukhipha okungaphezulu"</item>
<item msgid="2290859360633824369">"Bonisa izindawo ze-Deuteranomaly"</item>
</string-array>
- <string-array name="debug_hw_renderer_entries">
- <item msgid="2578620445459945681">"I-OpenGL (Okuzenzakalelayo)"</item>
- <item msgid="2839130076198120436">"I-OpenGL (Skia)"</item>
- </string-array>
<string-array name="app_process_limit_entries">
<item msgid="3401625457385943795">"Isilanganiso esivamile"</item>
<item msgid="4071574792028999443">"Azikho izinqubo zesizinda"</item>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index c5d6627..4f7f419 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -275,7 +275,6 @@
<string name="show_hw_layers_updates" msgid="5645728765605699821">"Bonisa izibuyekezo zezendlalelo izingxenyekazi zekhompyutha"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Izendlalelo zengxenyekazi zekhompyutha eziluhlaza uma zibuyekeza"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Susa iphutha lokukhipha okungaphezulu kwe-GPU"</string>
- <string name="debug_hw_renderer" msgid="7568529019431785816">"Setha isinikezeli se-GPU"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Khubaza izimbondela ze-HW"</string>
<string name="disable_overlays_summary" msgid="3578941133710758592">"Njalo sebenzisa i-GPU ukwakha isikrini"</string>
<string name="simulate_color_space" msgid="6745847141353345872">"Qoqa isikhala sombala"</string>
@@ -409,4 +408,10 @@
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Isikhathi esiningi."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Isikhathi esincane."</string>
+ <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Vula"</string>
+ <string name="cancel" msgid="6859253417269739139">"Khansela"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Vula ukungaphazamisi"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Soze"</string>
+ <string name="zen_interruption_level_priority" msgid="2078370238113347720">"Okubalulekile kuphela"</string>
+ <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index c926e1f..c1aa2dc 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -250,6 +250,19 @@
<item>Best Effort (Adaptive Bit Rate)</item>
</string-array>
+ <!-- TODO: Enable for translation per b/73007419 -->
+ <!-- Summaries for Bluetooth Audio Active Device status. [CHAR LIMIT=50]-->
+ <string-array name="bluetooth_audio_active_device_summaries" translatable="false" >
+ <!-- Status message when the device is not Active. -->
+ <item></item>
+ <!-- Status message when the device is Active for Media and Phone. -->
+ <item>, active</item>
+ <!-- Status message when the device is Active for Media only. -->
+ <item>, active(media)</item>
+ <!-- Status message when the device is Active for Phone only. -->
+ <item>, active(phone)</item>
+ </string-array>
+
<!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
<string-array name="select_logd_size_titles">
<item>Off</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 486a9bb..e557c65 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -128,27 +128,27 @@
<!-- Bluetooth settings. Message when connecting to a device -->
<string name="bluetooth_connecting">Connecting\u2026</string>
<!-- Bluetooth settings. Message when connected to a device. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected">Connected</string>
+ <string name="bluetooth_connected">Connected<xliff:g id="active_device">%1$s</xliff:g></string>
<!--Bluetooth settings screen, summary text under individual Bluetooth devices when pairing -->
<string name="bluetooth_pairing">Pairing\u2026</string>
<!-- Bluetooth settings. Message when connected to a device, except for phone audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_headset">Connected (no phone)</string>
+ <string name="bluetooth_connected_no_headset">Connected (no phone)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for media audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_a2dp">Connected (no media)</string>
+ <string name="bluetooth_connected_no_a2dp">Connected (no media)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for map. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_map">Connected (no message access)</string>
+ <string name="bluetooth_connected_no_map">Connected (no message access)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_no_headset_battery_level">Connected (no phone), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_no_headset_battery_level">Connected (no phone), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_no_a2dp_battery_level">Connected (no media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_no_a2dp_battery_level">Connected (no media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
<string name="bluetooth_profile_a2dp">Media audio</string>
@@ -865,30 +865,49 @@
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only">About <xliff:g id="time">^1</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">^1</xliff:g> left based on your usage</string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
- <string name="power_remaining_charging_duration_only"><xliff:g id="time">^1</xliff:g> left until fully charged</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
+ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
- <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time">^1</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short_enhanced"><xliff:g id="time">^1</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+ <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
- <string name="power_discharging_duration_enhanced"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
+ <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
+ <string name="power_remaining_less_than_duration"><xliff:g id="level">%1$s</xliff:g> - Less than <xliff:g id="threshold">%2$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration_short"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> left</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
+ <string name="power_remaining_more_than_subtext"><xliff:g id="level">%1$s</xliff:g>more than <xliff:g id="time_remaining">%2$s</xliff:g> remaining</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
+ <string name="power_remaining_only_more_than_subtext">more than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
+
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default">phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device">device may shutdown soon</string>
+
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+ <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
+ <string name="power_discharging_duration_enhanced"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left based on your usage</string>
+
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="default"><xliff:g id="level">%1$s</xliff:g> - phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet"><xliff:g id="level">%1$s</xliff:g> - tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="device"><xliff:g id="level">%1$s</xliff:g> - device may shutdown soon</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
- <string name="power_charging_duration"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> until fully charged</string>
+ <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
@@ -1039,5 +1058,13 @@
<string name="zen_interruption_level_priority">Priority only</string>
<!-- [CHAR LIMIT=20] Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. -->
<string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
+ <!-- Warning text when an alarm might be silenced by Do Not Disturb [CHAR LIMIT=NONE] -->
+ <string name="zen_alarm_warning_indef">You won\'t hear your next alarm <xliff:g id="when" example="at 7:00 AM">%1$s</xliff:g> unless you turn this off before then</string>
+ <!-- Warning text when an alarm might be silenced by Do Not Disturb due to a time-based condition [CHAR LIMIT=NONE] -->
+ <string name="zen_alarm_warning">You won\'t hear your next alarm <xliff:g id="when" example="at 7:00 AM">%1$s</xliff:g></string>
+ <!-- Alarm template for near alarms [CHAR LIMIT=25] -->
+ <string name="alarm_template">at <xliff:g id="when" example="7:00 AM">%1$s</xliff:g></string>
+ <!-- Alarm template for far in the future alarms [CHAR LIMIT=25] -->
+ <string name="alarm_template_far">on <xliff:g id="when" example="Fri 7:00 AM">%1$s</xliff:g></string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index fb0f75b..e1ebbc4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -940,60 +940,55 @@
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
- // TODO: A temporary workaround solution using string description the device is active.
- // Issue tracked by b/72317067 .
- // An alternative solution would be visual indication.
- // Intentionally not adding the strings to strings.xml for now:
- // 1) If this is just a short-term solution, no need to waste translation effort
- // 2) The number of strings with all possible combinations becomes enormously large.
- // If string description becomes part of the final solution, we MUST NOT
- // concatenate the strings here: this does not translate well.
- String activeString = null;
+ // Prepare the string for the Active Device summary
+ String[] activeDeviceStringsArray = mContext.getResources().getStringArray(
+ R.array.bluetooth_audio_active_device_summaries);
+ String activeDeviceString = activeDeviceStringsArray[0]; // Default value: not active
if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
- activeString = ", active";
+ activeDeviceString = activeDeviceStringsArray[1]; // Active for Media and Phone
} else {
if (mIsActiveDeviceA2dp) {
- activeString = ", active(media)";
+ activeDeviceString = activeDeviceStringsArray[2]; // Active for Media only
}
if (mIsActiveDeviceHeadset) {
- activeString = ", active(phone)";
+ activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
}
}
- if (activeString == null) activeString = "";
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
- activeString;
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp,
+ activeDeviceString);
}
} else if (a2dpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp,
+ activeDeviceString);
}
} else if (hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset)
- + activeString;
+ return mContext.getString(R.string.bluetooth_connected_no_headset,
+ activeDeviceString);
}
} else {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected) + activeString;
+ return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
old mode 100755
new mode 100644
index cda4e45..5f7ba586
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -194,8 +194,13 @@
return mState;
}
- synchronized void setBluetoothStateInt(int state) {
- mState = state;
+ void setBluetoothStateInt(int state) {
+ synchronized(this) {
+ if (mState == state) {
+ return;
+ }
+ mState = state;
+ }
if (state == BluetoothAdapter.STATE_ON) {
// if mProfileManager hasn't been constructed yet, it will
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 7983896..c23f226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -17,21 +17,20 @@
package com.android.settingslib.core.instrumentation;
import android.app.Activity;
-import android.content.Context;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Intent;
import android.os.SystemClock;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
/**
* Logs visibility change of a fragment.
*/
-public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause {
+public class VisibilityLoggerMixin implements LifecycleObserver {
private static final String TAG = "VisibilityLoggerMixin";
@@ -55,7 +54,7 @@
mMetricsFeature = metricsFeature;
}
- @Override
+ @OnLifecycleEvent(Event.ON_RESUME)
public void onResume() {
mVisibleTimestamp = SystemClock.elapsedRealtime();
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
@@ -63,7 +62,7 @@
}
}
- @Override
+ @OnLifecycleEvent(Event.ON_PAUSE)
public void onPause() {
mVisibleTimestamp = 0;
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
index 4e78d9b..85bf4e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.Build;
+import android.os.UserManager;
import android.provider.Settings;
import android.support.v4.content.LocalBroadcastManager;
@@ -27,7 +28,8 @@
public static final String DEVELOPMENT_SETTINGS_CHANGED_ACTION =
"com.android.settingslib.development.DevelopmentSettingsEnabler.SETTINGS_CHANGED";
- private DevelopmentSettingsEnabler() {}
+ private DevelopmentSettingsEnabler() {
+ }
public static void setDevelopmentSettingsEnabled(Context context, boolean enable) {
Settings.Global.putInt(context.getContentResolver(),
@@ -37,8 +39,14 @@
}
public static boolean isDevelopmentSettingsEnabled(Context context) {
- return Settings.Global.getInt(context.getContentResolver(),
+ final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ final boolean settingEnabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Build.TYPE.equals("eng") ? 1 : 0) != 0;
+ final boolean hasRestriction = um.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES);
+ final boolean isAdmin = um.isAdminUser();
+
+ return isAdmin && !hasRestriction && settingEnabled;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 190f5e6..68ead09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -17,7 +17,6 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
-import android.app.ActionBar;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -72,9 +71,9 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
super.setContentView(R.layout.settings_with_drawer);
- mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);
+ mContentHeaderContainer = findViewById(R.id.content_header_container);
- Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+ Toolbar toolbar = findViewById(R.id.action_bar);
if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
toolbar.setVisibility(View.GONE);
return;
@@ -89,7 +88,9 @@
@Override
public boolean onNavigateUp() {
- finish();
+ if (!super.onNavigateUp()) {
+ finish();
+ }
return true;
}
@@ -104,11 +105,6 @@
registerReceiver(mPackageReceiver, filter);
new CategoriesUpdateTask().execute();
- final Intent intent = getIntent();
- if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
- // Intent explicitly set to show menu.
- showMenuIcon();
- }
}
@Override
@@ -125,13 +121,6 @@
mCategoryListeners.remove(listener);
}
- public void setContentHeaderView(View headerView) {
- mContentHeaderContainer.removeAllViews();
- if (headerView != null) {
- mContentHeaderContainer.addView(headerView);
- }
- }
-
@Override
public void setContentView(@LayoutRes int layoutResID) {
final ViewGroup parent = findViewById(R.id.content_frame);
@@ -151,13 +140,6 @@
((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
}
- private void showMenuIcon() {
- final ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
- }
-
private void onCategoriesChanged() {
final int N = mCategoryListeners.size();
for (int i = 0; i < N; i++) {
@@ -165,10 +147,6 @@
}
}
- public void onProfileTileOpen() {
- finish();
- }
-
/**
* @return whether or not the enabled state actually changed.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index ed3696c..f7aa297 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -158,6 +158,9 @@
usage.startDate = start;
usage.usageLevel = totalBytes;
usage.period = formatDateRange(start, end);
+ usage.cycleStart = start;
+ usage.cycleEnd = end;
+
if (policy != null) {
usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
@@ -245,6 +248,8 @@
public long limitLevel;
public long warningLevel;
public long usageLevel;
+ public long cycleStart;
+ public long cycleEnd;
}
public interface Callback {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index a20f687..1a54d6a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -1,5 +1,3 @@
-package com.android.settingslib.notification;
-
/*
* Copyright (C) 2018 The Android Open Source Project
*
@@ -16,6 +14,8 @@
* limitations under the License.
*/
+package com.android.settingslib.notification;
+
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
@@ -28,6 +28,7 @@
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
+import android.text.format.DateFormat;
import android.util.Log;
import android.util.Slog;
import android.view.LayoutInflater;
@@ -40,6 +41,7 @@
import android.widget.ScrollView;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.policy.PhoneWindow;
@@ -48,11 +50,11 @@
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.Locale;
import java.util.Objects;
public class EnableZenModeDialog {
-
- private static final String TAG = "QSEnableZenModeDialog";
+ private static final String TAG = "EnableZenModeDialog";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
@@ -60,25 +62,37 @@
private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
- private static final int FOREVER_CONDITION_INDEX = 0;
- private static final int COUNTDOWN_CONDITION_INDEX = 1;
- private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
+ @VisibleForTesting
+ protected static final int FOREVER_CONDITION_INDEX = 0;
+ @VisibleForTesting
+ protected static final int COUNTDOWN_CONDITION_INDEX = 1;
+ @VisibleForTesting
+ protected static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
- private Uri mForeverId;
+ @VisibleForTesting
+ protected Uri mForeverId;
private int mBucketIndex = -1;
private AlarmManager mAlarmManager;
private int mUserId;
private boolean mAttached;
- private Context mContext;
+ @VisibleForTesting
+ protected Context mContext;
+ @VisibleForTesting
+ protected TextView mZenAlarmWarning;
+ @VisibleForTesting
+ protected LinearLayout mZenRadioGroupContent;
+
private RadioGroup mZenRadioGroup;
- private LinearLayout mZenRadioGroupContent;
private int MAX_MANUAL_DND_OPTIONS = 3;
+ @VisibleForTesting
+ protected LayoutInflater mLayoutInflater;
+
public EnableZenModeDialog(Context context) {
mContext = context;
}
@@ -133,32 +147,40 @@
for (int i = 0; i < N; i++) {
mZenRadioGroupContent.getChildAt(i).setVisibility(View.GONE);
}
+
+ mZenAlarmWarning.setVisibility(View.GONE);
}
protected View getContentView() {
- final LayoutInflater inflater = new PhoneWindow(mContext).getLayoutInflater();
- View contentView = inflater.inflate(R.layout.zen_mode_turn_on_dialog_container, null);
+ if (mLayoutInflater == null) {
+ mLayoutInflater = new PhoneWindow(mContext).getLayoutInflater();
+ }
+ View contentView = mLayoutInflater.inflate(R.layout.zen_mode_turn_on_dialog_container,
+ null);
ScrollView container = (ScrollView) contentView.findViewById(R.id.container);
mZenRadioGroup = container.findViewById(R.id.zen_radio_buttons);
mZenRadioGroupContent = container.findViewById(R.id.zen_radio_buttons_content);
+ mZenAlarmWarning = container.findViewById(R.id.zen_alarm_warning);
for (int i = 0; i < MAX_MANUAL_DND_OPTIONS; i++) {
- final View radioButton = inflater.inflate(R.layout.zen_mode_radio_button,
+ final View radioButton = mLayoutInflater.inflate(R.layout.zen_mode_radio_button,
mZenRadioGroup, false);
mZenRadioGroup.addView(radioButton);
radioButton.setId(i);
- final View radioButtonContent = inflater.inflate(R.layout.zen_mode_condition,
+ final View radioButtonContent = mLayoutInflater.inflate(R.layout.zen_mode_condition,
mZenRadioGroupContent, false);
radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS);
mZenRadioGroupContent.addView(radioButtonContent);
}
+
hideAllConditions();
return contentView;
}
- private void bind(final Condition condition, final View row, final int rowId) {
+ @VisibleForTesting
+ protected void bind(final Condition condition, final View row, final int rowId) {
if (condition == null) throw new IllegalArgumentException("condition must not be null");
final boolean enabled = condition.state == Condition.STATE_TRUE;
final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() :
@@ -181,6 +203,7 @@
if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId);
MetricsLogger.action(mContext,
MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT);
+ updateAlarmWarningText(tag.condition);
announceConditionSelection(tag);
}
}
@@ -190,11 +213,13 @@
row.setVisibility(View.VISIBLE);
}
- private ConditionTag getConditionTagAt(int index) {
+ @VisibleForTesting
+ protected ConditionTag getConditionTagAt(int index) {
return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
}
- private void bindConditions(Condition c) {
+ @VisibleForTesting
+ protected void bindConditions(Condition c) {
// forever
bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
FOREVER_CONDITION_INDEX);
@@ -236,11 +261,13 @@
return info != null ? info.getTriggerTime() : 0;
}
- private boolean isAlarm(Condition c) {
+ @VisibleForTesting
+ protected boolean isAlarm(Condition c) {
return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
}
- private boolean isCountdown(Condition c) {
+ @VisibleForTesting
+ protected boolean isCountdown(Condition c) {
return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
}
@@ -264,7 +291,8 @@
}
// Returns a time condition if the next alarm is within the next week.
- private Condition getTimeUntilNextAlarmCondition() {
+ @VisibleForTesting
+ protected Condition getTimeUntilNextAlarmCondition() {
GregorianCalendar weekRange = new GregorianCalendar();
setToMidnight(weekRange);
weekRange.add(Calendar.DATE, 6);
@@ -282,7 +310,8 @@
return null;
}
- private void bindGenericCountdown() {
+ @VisibleForTesting
+ protected void bindGenericCountdown() {
mBucketIndex = DEFAULT_BUCKET_INDEX;
Condition countdown = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
@@ -366,7 +395,8 @@
}
}
- private void bindNextAlarm(Condition c) {
+ @VisibleForTesting
+ protected void bindNextAlarm(Condition c) {
View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
ConditionTag tag = (ConditionTag) alarmContent.getTag();
@@ -415,6 +445,7 @@
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
bind(newCondition, row, rowId);
+ updateAlarmWarningText(tag.condition);
tag.rb.setChecked(true);
announceConditionSelection(tag);
}
@@ -428,8 +459,43 @@
}
}
+ private void updateAlarmWarningText(Condition condition) {
+ String warningText = computeAlarmWarningText(condition);
+ mZenAlarmWarning.setText(warningText);
+ mZenAlarmWarning.setVisibility(warningText == null ? View.GONE : View.VISIBLE);
+ }
+
+ private String computeAlarmWarningText(Condition condition) {
+ final long now = System.currentTimeMillis();
+ final long nextAlarm = getNextAlarm();
+ if (nextAlarm < now) {
+ return null;
+ }
+ int warningRes = 0;
+ if (condition == null || isForever(condition)) {
+ warningRes = R.string.zen_alarm_warning_indef;
+ } else {
+ final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id);
+ if (time > now && nextAlarm < time) {
+ warningRes = R.string.zen_alarm_warning;
+ }
+ }
+ if (warningRes == 0) {
+ return null;
+ }
+ final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000;
+ final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
+ final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma");
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm);
+ final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far;
+ final String template = mContext.getResources().getString(templateRes, formattedTime);
+ return mContext.getResources().getString(warningRes, template);
+ }
+
// used as the view tag on condition rows
- private static class ConditionTag {
+ @VisibleForTesting
+ protected static class ConditionTag {
public RadioButton rb;
public View lines;
public TextView line1;
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
new file mode 100644
index 0000000..346ca66
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -0,0 +1,143 @@
+/*
+ * 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.settingslib.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.settingslib.R;
+import com.android.settingslib.utils.StringUtil;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/** Utility class for keeping power related strings consistent**/
+public class PowerUtil {
+ private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
+ private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15);
+ private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+ /**
+ * This method produces the text used in various places throughout the system to describe the
+ * remaining battery life of the phone in a consistent manner.
+ *
+ * @param context
+ * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
+ * @param percentageString An optional percentage of battery remaining string.
+ * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation.
+ * @return a properly formatted and localized string describing how much time remains
+ * before the battery runs out.
+ */
+ public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs,
+ @Nullable String percentageString, boolean basedOnUsage) {
+ if (drainTimeMs > 0) {
+ if (drainTimeMs <= SEVEN_MINUTES_MILLIS) {
+ // show a imminent shutdown warning if less than 7 minutes remain
+ return getShutdownImminentString(context, percentageString);
+ } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) {
+ // show a less than 15 min remaining warning if appropriate
+ CharSequence timeString = StringUtil.formatElapsedTime(context,
+ FIFTEEN_MINUTES_MILLIS,
+ false /* withSeconds */);
+ return getUnderFifteenString(context, timeString, percentageString);
+ } else if (drainTimeMs >= ONE_DAY_MILLIS) {
+ // just say more than one day if over 24 hours
+ return getMoreThanOneDayString(context, percentageString);
+ } else {
+ // show a regular time remaining string
+ return getRegularTimeRemainingString(context, drainTimeMs,
+ percentageString, basedOnUsage);
+ }
+ }
+ return null;
+ }
+
+ private static String getShutdownImminentString(Context context, String percentageString) {
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
+ : context.getString(
+ R.string.power_remaining_duration_shutdown_imminent,
+ percentageString);
+ }
+
+ private static String getUnderFifteenString(Context context, CharSequence timeString,
+ String percentageString) {
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_less_than_duration_only, timeString)
+ : context.getString(
+ R.string.power_remaining_less_than_duration,
+ percentageString,
+ timeString);
+
+ }
+
+ private static String getMoreThanOneDayString(Context context, String percentageString) {
+ final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
+
+ final Measure daysMeasure = new Measure(1, MeasureUnit.DAY);
+
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_only_more_than_subtext,
+ frmt.formatMeasures(daysMeasure))
+ : context.getString(
+ R.string.power_remaining_more_than_subtext,
+ percentageString,
+ frmt.formatMeasures(daysMeasure));
+ }
+
+ private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ String percentageString, boolean basedOnUsage) {
+ // round to the nearest 15 min to not appear oversly precise
+ final long roundedTimeMs = roundToNearestThreshold(drainTimeMs,
+ FIFTEEN_MINUTES_MILLIS);
+ CharSequence timeString = StringUtil.formatElapsedTime(context,
+ roundedTimeMs,
+ false /* withSeconds */);
+ if (TextUtils.isEmpty(percentageString)) {
+ int id = basedOnUsage
+ ? R.string.power_remaining_duration_only_enhanced
+ : R.string.power_remaining_duration_only;
+ return context.getString(id, timeString);
+ } else {
+ int id = basedOnUsage
+ ? R.string.power_discharging_duration_enhanced
+ : R.string.power_discharging_duration;
+ return context.getString(id, percentageString, timeString);
+ }
+ }
+
+ public static long convertUsToMs(long timeUs) {
+ return timeUs / 1000;
+ }
+
+ public static long convertMsToUs(long timeMs) {
+ return timeMs * 1000;
+ }
+
+ private static long roundToNearestThreshold(long drainTime, long threshold) {
+ final long remainder = drainTime % threshold;
+ if (remainder < threshold / 2) {
+ return drainTime - remainder;
+ } else {
+ return drainTime - remainder + threshold;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
new file mode 100644
index 0000000..45fdd78
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -0,0 +1,149 @@
+/*
+ * 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.settingslib.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.text.RelativeDateTimeFormatter;
+import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.icu.util.ULocale;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TtsSpan;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/** Utility class for generally useful string methods **/
+public class StringUtil {
+
+ public static final int SECONDS_PER_MINUTE = 60;
+ public static final int SECONDS_PER_HOUR = 60 * 60;
+ public static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 2d 5h 40m 29s
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatElapsedTime(Context context, double millis,
+ boolean withSeconds) {
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ int seconds = (int) Math.floor(millis / 1000);
+ if (!withSeconds) {
+ // Round up.
+ seconds += 30;
+ }
+
+ int days = 0, hours = 0, minutes = 0;
+ if (seconds >= SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds >= SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds >= SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ final ArrayList<Measure> measureList = new ArrayList(4);
+ if (days > 0) {
+ measureList.add(new Measure(days, MeasureUnit.DAY));
+ }
+ if (hours > 0) {
+ measureList.add(new Measure(hours, MeasureUnit.HOUR));
+ }
+ if (minutes > 0) {
+ measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
+ }
+ if (withSeconds && seconds > 0) {
+ measureList.add(new Measure(seconds, MeasureUnit.SECOND));
+ }
+ if (measureList.size() == 0) {
+ // Everything addable was zero, so nothing was added. We add a zero.
+ measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
+ }
+ final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
+
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final MeasureFormat measureFormat = MeasureFormat.getInstance(
+ locale, FormatWidth.NARROW);
+ sb.append(measureFormat.formatMeasures(measureArray));
+
+ if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
+ // Add ttsSpan if it only have minute value, because it will be read as "meters"
+ final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+ .setUnit("minute").build();
+ sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ return sb;
+ }
+
+ /**
+ * Returns relative time for the given millis in the past, in a short format such as "2 days
+ * ago", "5 hr. ago", "40 min. ago", or "29 sec. ago".
+ *
+ * <p>The unit is chosen to have good information value while only using one unit. So 27 hours
+ * and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as
+ * "2 days ago".
+ *
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatRelativeTime(Context context, double millis,
+ boolean withSeconds) {
+ final int seconds = (int) Math.floor(millis / 1000);
+ final RelativeUnit unit;
+ final int value;
+ if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
+ unit = RelativeUnit.SECONDS;
+ value = seconds;
+ } else if (seconds < 2 * SECONDS_PER_HOUR) {
+ unit = RelativeUnit.MINUTES;
+ value = (seconds + SECONDS_PER_MINUTE / 2)
+ / SECONDS_PER_MINUTE;
+ } else if (seconds < 2 * SECONDS_PER_DAY) {
+ unit = RelativeUnit.HOURS;
+ value = (seconds + SECONDS_PER_HOUR / 2)
+ / SECONDS_PER_HOUR;
+ } else {
+ unit = RelativeUnit.DAYS;
+ value = (seconds + SECONDS_PER_DAY / 2)
+ / SECONDS_PER_DAY;
+ }
+
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
+ ULocale.forLocale(locale),
+ null /* default NumberFormat */,
+ RelativeDateTimeFormatter.Style.SHORT,
+ android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
+
+ return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index e11017c..f699440 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -622,6 +622,19 @@
return builder.toString();
}
+ public static String getKey(WifiConfiguration config) {
+ StringBuilder builder = new StringBuilder();
+
+ if (TextUtils.isEmpty(config.SSID)) {
+ builder.append(config.BSSID);
+ } else {
+ builder.append(removeDoubleQuotes(config.SSID));
+ }
+
+ builder.append(',').append(getSecurity(config));
+ return builder.toString();
+ }
+
public String getKey() {
return mKey;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 109eb97..8115ede2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -41,7 +41,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.wifi.AccessPoint.Speed;
-public class AccessPointPreference extends TwoTargetPreference {
+public class AccessPointPreference extends Preference {
private static final int[] STATE_SECURED = {
R.attr.state_encrypted
@@ -115,6 +115,7 @@
int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld,
int level, IconInjector iconInjector) {
super(context);
+ setWidgetLayoutResource(R.layout.access_point_friction_widget);
mBadgeCache = cache;
mAccessPoint = accessPoint;
mForSavedNetworks = forSavedNetworks;
@@ -153,20 +154,6 @@
ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
bindFrictionImage(frictionImageView);
- setDividerVisibility(view, View.GONE);
- }
-
- protected void setDividerVisibility(final PreferenceViewHolder view,
- @View.Visibility int visibility) {
- final View divider = view.findViewById(R.id.two_target_divider);
- if (divider != null) {
- divider.setVisibility(visibility);
- }
- }
-
- @Override
- protected int getSecondTargetResId() {
- return R.layout.access_point_friction_widget;
}
protected void updateIcon(int level, Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 1ac56a9..fac585e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -92,8 +92,6 @@
* and used so as to assist with in-the-field WiFi connectivity debugging */
public static boolean sVerboseLogging;
- // TODO(b/36733768): Remove flag includeSaved
-
// TODO: Allow control of this?
// Combo scans can take 5-6s to complete - set to 10s.
private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
@@ -106,8 +104,6 @@
private final NetworkRequest mNetworkRequest;
private final AtomicBoolean mConnected = new AtomicBoolean(false);
private final WifiListener mListener;
- private final boolean mIncludeSaved;
- private final boolean mIncludeScans;
@VisibleForTesting MainHandler mMainHandler;
@VisibleForTesting WorkHandler mWorkHandler;
private HandlerThread mWorkThread;
@@ -150,7 +146,6 @@
// TODO(sghuman): Change this to be keyed on AccessPoint.getKey
private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
- private Integer mScanId = 0;
private NetworkInfo mLastNetworkInfo;
private WifiInfo mLastInfo;
@@ -189,16 +184,18 @@
@Deprecated
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, includeSaved, includeScans,
+ this(context, wifiListener,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
newIntentFilter());
}
+ // TODO(Sghuman): Clean up includeSaved and includeScans from all constructors and linked
+ // calling apps once IC window is complete
public WifiTracker(Context context, WifiListener wifiListener,
@NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
- this(context, wifiListener, includeSaved, includeScans,
+ this(context, wifiListener,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
@@ -208,19 +205,13 @@
@VisibleForTesting
WifiTracker(Context context, WifiListener wifiListener,
- boolean includeSaved, boolean includeScans,
WifiManager wifiManager, ConnectivityManager connectivityManager,
NetworkScoreManager networkScoreManager,
IntentFilter filter) {
- if (!includeSaved && !includeScans) {
- throw new IllegalArgumentException("Must include either saved or scans");
- }
mContext = context;
mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
- mIncludeSaved = includeSaved;
- mIncludeScans = includeScans;
- mListener = wifiListener;
+ mListener = new WifiListenerWrapper(wifiListener);
mConnectivityManager = connectivityManager;
// check if verbose logging has been turned on or off
@@ -458,7 +449,6 @@
private void handleResume() {
mScanResultCache.clear();
mSeenBssids.clear();
- mScanId = 0;
}
private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) {
@@ -533,7 +523,7 @@
/**
* Update the internal list of access points.
*
- * <p>Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which
+ * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which
* respects {@link #mStaleScanResults}.
*/
@GuardedBy("mLock")
@@ -542,7 +532,7 @@
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(
- mLastInfo.getNetworkId(), mWifiManager.getConfiguredNetworks());
+ mLastInfo.getNetworkId(), configs);
}
// Swap the current access points into a cached list.
@@ -554,43 +544,12 @@
accessPoint.clearConfig();
}
- /* Lookup table to more quickly update AccessPoints by only considering objects with the
- * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */
- Multimap<String, AccessPoint> existingApMap = new Multimap<String, AccessPoint>();
-
final Collection<ScanResult> results = updateScanResultCache(newScanResults);
- // TODO(sghuman): This entire block only exists to populate the WifiConfiguration for
- // APs, remove and refactor
+ final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
if (configs != null) {
for (WifiConfiguration config : configs) {
- if (config.selfAdded && config.numAssociation == 0) {
- continue;
- }
- AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints);
- if (mLastInfo != null && mLastNetworkInfo != null) {
- accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
- }
- if (mIncludeSaved) {
- // If saved network not present in scan result then set its Rssi to
- // UNREACHABLE_RSSI
- boolean apFound = false;
- for (ScanResult result : results) {
- if (result.SSID.equals(accessPoint.getSsidStr())) {
- apFound = true;
- break;
- }
- }
- if (!apFound) {
- accessPoint.setUnreachable();
- }
- accessPoints.add(accessPoint);
- existingApMap.put(accessPoint.getSsidStr(), accessPoint);
- } else {
- // If we aren't using saved networks, drop them into the cache so that
- // we have access to their saved info.
- cachedAccessPoints.add(accessPoint);
- }
+ configsByKey.put(AccessPoint.getKey(config), config);
}
}
@@ -626,40 +585,20 @@
for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
// List can not be empty as it is dynamically constructed on each iteration
ScanResult firstResult = entry.getValue().get(0);
- boolean found = false;
- for (AccessPoint accessPoint : existingApMap.getAll(firstResult.SSID)) {
- accessPoint.setScanResults(entry.getValue());
- found = true;
- break;
+
+ AccessPoint accessPoint =
+ getCachedOrCreate(entry.getValue(), cachedAccessPoints);
+ if (mLastInfo != null && mLastNetworkInfo != null) {
+ accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
- // Only create a new AP / add to the list if it wasn't already in the saved configs
- if (!found) {
- AccessPoint accessPoint =
- getCachedOrCreate(entry.getValue(), cachedAccessPoints);
- if (mLastInfo != null && mLastNetworkInfo != null) {
- accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
- }
-
- // TODO(sghuman): Move isPasspointNetwork logic into AccessPoint.java
- if (firstResult.isPasspointNetwork()) {
- // Retrieve a WifiConfiguration for a Passpoint provider that matches
- // the given ScanResult. This is used for showing that a given AP
- // (ScanResult) is available via a Passpoint provider (provider friendly
- // name).
- try {
- WifiConfiguration config =
- mWifiManager.getMatchingWifiConfig(firstResult);
- if (config != null) {
- accessPoint.update(config);
- }
- } catch (UnsupportedOperationException e) {
- // Passpoint not supported on the device.
- }
- }
-
- accessPoints.add(accessPoint);
+ // Update the matching config if there is one, to populate saved network info
+ WifiConfiguration config = configsByKey.get(entry.getKey());
+ if (config != null) {
+ accessPoint.update(config);
}
+
+ accessPoints.add(accessPoint);
}
}
@@ -1052,6 +991,39 @@
}
}
+ /**
+ * Wraps the given {@link WifiListener} instance and executes it's methods on the Main Thread.
+ *
+ * <p>This mechanism allows us to no longer need a separate MainHandler and WorkHandler, which
+ * were previously both performing work, while avoiding errors which occur from executing
+ * callbacks which manipulate UI elements from a different thread than the MainThread.
+ */
+ private static class WifiListenerWrapper implements WifiListener {
+
+ private final Handler mHandler;
+ private final WifiListener mDelegatee;
+
+ public WifiListenerWrapper(WifiListener listener) {
+ mHandler = new Handler(Looper.getMainLooper());
+ mDelegatee = listener;
+ }
+
+ @Override
+ public void onWifiStateChanged(int state) {
+ mHandler.post(() -> mDelegatee.onWifiStateChanged(state));
+ }
+
+ @Override
+ public void onConnectedChanged() {
+ mHandler.post(() -> mDelegatee.onConnectedChanged());
+ }
+
+ @Override
+ public void onAccessPointsChanged() {
+ mHandler.post(() -> mDelegatee.onAccessPointsChanged());
+ }
+ }
+
public interface WifiListener {
/**
* Called when the state of Wifi has changed, the state will be one of
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
index 003f905..2f417ad 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
@@ -18,8 +18,6 @@
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import android.app.Instrumentation;
@@ -49,42 +47,22 @@
}
@Test
- public void startActivityWithNoExtra_showNoNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- instrumentation.startActivitySync(new Intent(instrumentation.getTargetContext(),
- TestActivity.class));
-
- onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
- .check(doesNotExist());
- }
-
- @Test
- public void startActivityWithExtraToHideMenu_showNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
- .putExtra(TestActivity.EXTRA_SHOW_MENU, false);
+ public void startActivity_doNotShowNavUp() {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
instrumentation.startActivitySync(intent);
onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
.check(doesNotExist());
}
- @Test
- public void startActivityWithExtraToShowMenu_showNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
- .putExtra(TestActivity.EXTRA_SHOW_MENU, true);
- instrumentation.startActivitySync(intent);
-
- onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
- .check(matches(isDisplayed()));
- }
-
/**
* Test Activity in this test.
*
* Use this activity because SettingsDrawerActivity hasn't been registered in its
* AndroidManifest.xml
*/
- public static class TestActivity extends SettingsDrawerActivity {}
+ public static class TestActivity extends SettingsDrawerActivity {
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index b36dda9..6be4936 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -281,8 +281,6 @@
final WifiTracker wifiTracker = new WifiTracker(
mContext,
mockWifiListener,
- true,
- true,
mockWifiManager,
mockConnectivityManager,
mockNetworkScoreManager,
@@ -687,6 +685,7 @@
*/
@Test
public void trackPasspointApWithPasspointDisabled() throws Exception {
+ // TODO(sghuman): Delete this test and replace with a passpoint test
WifiTracker tracker = createMockedWifiTracker();
// Add a Passpoint AP to the scan results.
@@ -709,10 +708,7 @@
when(mockWifiManager.getConfiguredNetworks())
.thenReturn(new ArrayList<WifiConfiguration>());
when(mockWifiManager.getScanResults()).thenReturn(results);
- doThrow(new UnsupportedOperationException())
- .when(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class));
tracker.forceUpdate();
- verify(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class));
}
@Test
@@ -758,7 +754,7 @@
tracker.forceUpdate();
verify(mockWifiManager).getConnectionInfo();
- verify(mockWifiManager, times(2)).getConfiguredNetworks();
+ verify(mockWifiManager, times(1)).getConfiguredNetworks();
verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
verify(mockWifiListener, never()).onAccessPointsChanged(); // mStaleAccessPoints is true
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 4091ce1..1481161 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -80,22 +80,12 @@
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
}
- /**
- * Test to verify the current test context object works so that we are not checking null
- * against null
- */
- @Test
- public void testContextMock() {
- assertThat(mContext.getString(R.string.bluetooth_connected)).isEqualTo("Connected");
- }
-
@Test
public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -105,9 +95,7 @@
mBatteryLevel = 10;
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -118,8 +106,7 @@
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -134,28 +121,23 @@
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
// Disconnect HFP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_headset_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%");
// Disconnect A2DP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_a2dp_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%");
// Disconnect both HFP and A2DP and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone or media), battery 10%");
// Disconnect all profiles and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -163,6 +145,117 @@
}
@Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+ // Test without battery level
+ // Set A2DP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active(media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() {
+ // Test without battery level
+ // Set HFP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for HFP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active(phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set HFP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
+ // Test without battery level
+ // Set A2DP and HFP profiles to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and HFP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active");
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%, active(phone)");
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.setActiveDevice(false, BluetoothProfile.HEADSET);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%, active(media)");
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP and HFP profiles to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Set A2DP and HFP profiles to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
public void testDeviceName_testAliasNameAvailable() {
when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
when(mDevice.getName()).thenReturn(DEVICE_NAME);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index a264886..1ab6afe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -18,6 +18,7 @@
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -30,6 +31,8 @@
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
@@ -39,6 +42,8 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
@@ -110,6 +115,30 @@
.hidden(nullable(Context.class), anyInt());
}
+ @Test
+ public void activityShouldBecomeVisibleAndHide() {
+ ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
+ TestActivity testActivity = ac.get();
+ MockitoAnnotations.initMocks(testActivity);
+ ac.create().start().resume();
+ verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt());
+ ac.pause().stop().destroy();
+ verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt());
+ }
+
+ public static class TestActivity extends FragmentActivity {
+ @Mock
+ MetricsFeatureProvider mMetricsFeatureProvider;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ VisibilityLoggerMixin mixin = new VisibilityLoggerMixin(
+ TestInstrumentable.TEST_METRIC, mMetricsFeatureProvider);
+ getLifecycle().addObserver(mixin);
+ super.onCreate(savedInstanceState);
+ }
+ }
+
private final class TestInstrumentable implements Instrumentable {
public static final int TEST_METRIC = 12345;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index d19d19a..708353e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -23,7 +23,9 @@
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +33,9 @@
import org.robolectric.annotation.Config;
@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = {
+ ShadowUserManager.class
+})
public class DevelopmentSettingsEnablerTest {
private Context mContext;
@@ -39,10 +43,16 @@
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
+ ShadowUserManager.getShadow().setIsAdminUser(true);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUserManager.getShadow().reset();
}
@Test
- public void testEnabling() {
+ public void isEnabled_settingsOn_noRestriction_isAdmin_shouldReturnTrue() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
@@ -54,7 +64,7 @@
}
@Test
- public void testDisabling() {
+ public void isEnabled_settingsOff_noRestriction_isAdmin_shouldReturnFalse() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
@@ -64,4 +74,13 @@
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
}
+
+ @Test
+ public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+ ShadowUserManager.getShadow().setIsAdminUser(false);
+
+ assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
new file mode 100644
index 0000000..777cd98
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.settingslib.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.view.LayoutInflater;
+
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EnableZenModeDialogTest {
+ private EnableZenModeDialog mController;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Fragment mFragment;
+
+ private Context mShadowContext;
+ private LayoutInflater mLayoutInflater;
+ private Condition mCountdownCondition;
+ private Condition mAlarmCondition;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mShadowContext = RuntimeEnvironment.application;
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mFragment.getContext()).thenReturn(mShadowContext);
+ mLayoutInflater = LayoutInflater.from(mShadowContext);
+
+ mController = spy(new EnableZenModeDialog(mContext));
+ mController.mContext = mContext;
+ mController.mLayoutInflater = mLayoutInflater;
+ mController.mForeverId = Condition.newId(mContext).appendPath("forever").build();
+ when(mContext.getString(com.android.internal.R.string.zen_mode_forever))
+ .thenReturn("testSummary");
+ mController.getContentView();
+
+ // these methods use static calls to ZenModeConfig which would normally fail in robotests,
+ // so instead do nothing:
+ doNothing().when(mController).bindGenericCountdown();
+ doReturn(null).when(mController).getTimeUntilNextAlarmCondition();
+ doReturn(0L).when(mController).getNextAlarm();
+ doNothing().when(mController).bindNextAlarm(any());
+
+ // as a result of doing nothing above, must bind manually:
+ Uri alarm = Condition.newId(mContext).appendPath("alarm").build();
+ mAlarmCondition = new Condition(alarm, "alarm", "", "", 0, 0, 0);
+ Uri countdown = Condition.newId(mContext).appendPath("countdown").build();
+ mCountdownCondition = new Condition(countdown, "countdown", "", "", 0, 0, 0);
+ mController.bind(mCountdownCondition,
+ mController.mZenRadioGroupContent.getChildAt(
+ EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX),
+ EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX);
+ mController.bind(mAlarmCondition,
+ mController.mZenRadioGroupContent.getChildAt(
+ EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX),
+ EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX);
+ }
+
+ @Test
+ public void testForeverChecked() {
+ mController.bindConditions(mController.forever());
+
+ assertTrue(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
+ .isChecked());
+ assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
+ .isChecked());
+ assertFalse(mController.getConditionTagAt(
+ EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ }
+
+ @Test
+ public void testNoneChecked() {
+ mController.bindConditions(null);
+ assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
+ .isChecked());
+ assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
+ .isChecked());
+ assertFalse(mController.getConditionTagAt(
+ EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ }
+
+ @Test
+ public void testAlarmChecked() {
+ doReturn(false).when(mController).isCountdown(mAlarmCondition);
+ doReturn(true).when(mController).isAlarm(mAlarmCondition);
+
+ mController.bindConditions(mAlarmCondition);
+ assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
+ .isChecked());
+ assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
+ .isChecked());
+ assertTrue(mController.getConditionTagAt(
+ EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ }
+
+ @Test
+ public void testCountdownChecked() {
+ doReturn(false).when(mController).isAlarm(mCountdownCondition);
+ doReturn(true).when(mController).isCountdown(mCountdownCondition);
+
+ mController.bindConditions(mCountdownCondition);
+ assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
+ .isChecked());
+ assertTrue(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
+ .isChecked());
+ assertFalse(mController.getConditionTagAt(
+ EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
new file mode 100644
index 0000000..c8b3269
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.testutils.shadow;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.shadow.api.Shadow;
+
+@Implements(UserManager.class)
+public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
+
+ private boolean mAdminUser;
+
+ public void setIsAdminUser(boolean isAdminUser) {
+ mAdminUser = isAdminUser;
+ }
+
+ @Resetter
+ public void reset() {
+ mAdminUser = false;
+ }
+
+ @Implementation
+ public boolean isAdminUser() {
+ return mAdminUser;
+ }
+
+ @Implementation
+ public static UserManager get(Context context) {
+ return (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ public static ShadowUserManager getShadow() {
+ return (ShadowUserManager) Shadow.extract(
+ RuntimeEnvironment.application.getSystemService(UserManager.class));
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
new file mode 100644
index 0000000..f93210f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.settingslib.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.utils.PowerUtil;
+import java.time.Duration;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PowerUtilTest {
+ public static final String TEST_BATTERY_LEVEL_10 = "10%";
+ public static final String FIFTEEN_MIN_FORMATTED = "15m";
+ public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+ public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+ public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+ public static final long TWO_DAYS_MILLIS = Duration.ofDays(2).toMillis();
+ public static final String ONE_DAY_FORMATTED = "1 day";
+
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ TEST_BATTERY_LEVEL_10,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ TEST_BATTERY_LEVEL_10,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_discharging_duration_enhanced,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_discharging_duration,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_noPercentage() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ null /* percentageString */,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only_enhanced,
+ FIFTEEN_MIN_FORMATTED));
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ FIVE_MINUTES_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ FIVE_MINUTES_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+
+ // additional battery percentage in this string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_shutdown_imminent,
+ TEST_BATTERY_LEVEL_10));
+ // shortened string should not have percentage
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only_shutdown_imminent));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_betweenSevenAndFifteenMinutes_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TEN_MINUTES_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TEN_MINUTES_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+
+ // shortened string should not have percentage
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_less_than_duration_only,
+ FIFTEEN_MIN_FORMATTED));
+ // Add percentage to string when provided
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_less_than_duration,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanOneDay_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TWO_DAYS_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TWO_DAYS_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+
+ // shortened string should not have percentage
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_only_more_than_subtext,
+ ONE_DAY_FORMATTED));
+ // Add percentage to string when provided
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_more_than_subtext,
+ TEST_BATTERY_LEVEL_10,
+ ONE_DAY_FORMATTED));
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
new file mode 100644
index 0000000..d5e3cdb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.settingslib.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class StringUtilTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
+ final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "5m 30s";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
+ final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "6m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_TimeMoreThanOneDay_ShowCorrectly() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
+ + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2d 4h 15m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_ZeroFieldsInTheMiddleDontShow() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2d 15m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_FormatZero_WithSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0s";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_FormatZero_NoSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
+ final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
+
+ final CharSequence charSequence =
+ StringUtil.formatElapsedTime(mContext, testMillis, false);
+ assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
+
+ final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
+ final TtsSpan[] ttsSpans = expectedString.getSpans(0, expectedString.length(),
+ TtsSpan.class);
+
+ assertThat(ttsSpans).asList().hasSize(1);
+ assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE);
+ }
+
+ @Test
+ public void testFormatRelativeTime_WithSeconds_ShowSeconds() {
+ final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "40 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() {
+ final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "1 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() {
+ final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "119 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() {
+ final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "2 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoMinutes_withSeconds() {
+ final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoHours_withSeconds() {
+ final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "119 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoHours_withSeconds() {
+ final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS;
+ final String expectedTime = "2 hr. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoDays_withSeconds() {
+ final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS;
+ final String expectedTime = "47 hr. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoDays_withSeconds() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS;
+ final String expectedTime = "2 days ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_FormatZero_WithSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_FormatZero_NoSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 1dc8e46..cfcfb95 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -43,7 +43,6 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.ims.ImsConfig;
import com.android.internal.content.PackageHelper;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RILConstants;
@@ -1504,7 +1503,7 @@
try {
stmt = db.compileStatement("INSERT OR REPLACE INTO global(name,value)"
+ " VALUES(?,?);");
- loadStringSetting(stmt, Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
+ loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND,
R.string.def_wireless_charging_started_sound);
db.setTransactionSuccessful();
} finally {
@@ -2578,7 +2577,7 @@
R.string.def_car_dock_sound);
loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND,
R.string.def_car_undock_sound);
- loadStringSetting(stmt, Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
+ loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND,
R.string.def_wireless_charging_started_sound);
loadIntegerSetting(stmt, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index b286f89..39f2b52 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -160,7 +160,7 @@
Settings.Global.POWER_SOUNDS_ENABLED,
GlobalSettingsProto.POWER_SOUNDS_ENABLED);
dumpSetting(s, p,
- Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
+ Settings.Global.CHARGING_STARTED_SOUND,
GlobalSettingsProto.WIRELESS_CHARGING_STARTED_SOUND);
dumpSetting(s, p,
Settings.Global.CHARGING_SOUNDS_ENABLED,
@@ -523,9 +523,6 @@
Settings.Global.WIFI_WAKEUP_ENABLED,
GlobalSettingsProto.WIFI_WAKEUP_ENABLED);
dumpSetting(s, p,
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
- GlobalSettingsProto.WIFI_WAKEUP_AVAILABLE);
- dumpSetting(s, p,
Settings.Global.NETWORK_SCORING_UI_ENABLED,
GlobalSettingsProto.NETWORK_SCORING_UI_ENABLED);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d1459bb..85a579d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2328,7 +2328,9 @@
// Get all uids for the user's packages.
final List<PackageInfo> packages;
try {
- packages = mPackageManager.getInstalledPackages(0, user.id).getList();
+ packages = mPackageManager.getInstalledPackages(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ user.id).getList();
} catch (RemoteException e) {
throw new IllegalStateException("Package manager not available");
}
@@ -3015,7 +3017,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 152;
+ private static final int SETTINGS_VERSION = 154;
private final int mUserId;
@@ -3401,7 +3403,9 @@
// Fill each uid with the legacy ssaid to be backwards compatible.
final List<PackageInfo> packages;
try {
- packages = mPackageManager.getInstalledPackages(0, userId).getList();
+ packages = mPackageManager.getInstalledPackages(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ userId).getList();
} catch (RemoteException e) {
throw new IllegalStateException("Package manager not available");
}
@@ -3416,6 +3420,9 @@
// Android Id doesn't exist for this package so create it.
ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true,
info.packageName);
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Keep the legacy ssaid for uid=" + uid);
+ }
}
}
}
@@ -3540,21 +3547,9 @@
}
if (currentVersion == 146) {
- // Version 147: Set the default value for WIFI_WAKEUP_AVAILABLE.
- if (userId == UserHandle.USER_SYSTEM) {
- final SettingsState globalSettings = getGlobalSettingsLocked();
- final Setting currentSetting = globalSettings.getSettingLocked(
- Settings.Global.WIFI_WAKEUP_AVAILABLE);
- if (currentSetting.getValue() == null) {
- final int defaultValue = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_wifi_wakeup_available);
- globalSettings.insertSettingLocked(
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
- String.valueOf(defaultValue),
- null, true, SettingsState.SYSTEM_PACKAGE_NAME);
- }
- }
-
+ // Version 147: Removed. (This version previously allowed showing the
+ // "wifi_wakeup_available" setting).
+ // The setting that was added here is deleted in 153.
currentVersion = 147;
}
@@ -3621,18 +3616,34 @@
}
if (currentVersion == 151) {
- // Version 152: Reset wifi wake available for upgrading users
- final SettingsState globalSettings = getGlobalSettingsLocked();
- final int defaultValue = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_wifi_wakeup_available);
- globalSettings.insertSettingLocked(
- Settings.Global.WIFI_WAKEUP_AVAILABLE,
- String.valueOf(defaultValue),
- null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-
+ // Version 152: Removed. (This version made the setting for wifi_wakeup enabled
+ // by default but it is now no longer configurable).
+ // The setting updated here is deleted in 153.
currentVersion = 152;
}
+ if (currentVersion == 152) {
+ getGlobalSettingsLocked().deleteSettingLocked("wifi_wakeup_available");
+ currentVersion = 153;
+ }
+
+ if (currentVersion == 153) {
+ // Version 154: Read notification badge configuration from config.
+ // If user has already set the value, don't do anything.
+ final SettingsState systemSecureSettings = getSecureSettingsLocked(userId);
+ final Setting showNotificationBadges = systemSecureSettings.getSettingLocked(
+ Settings.Secure.NOTIFICATION_BADGING);
+ if (showNotificationBadges.isNull()) {
+ final boolean defaultValue = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_notificationBadging);
+ systemSecureSettings.insertSettingLocked(
+ Secure.NOTIFICATION_BADGING,
+ defaultValue ? "1" : "0",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 154;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2bcf4ef..1e48213 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -56,7 +56,6 @@
SystemUI-proto
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_JAVA_LIBRARIES += android.car
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9613a6a..cbb3e8f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -176,9 +176,6 @@
<!-- It's like, reality, but, you know, virtual -->
<uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
- <!-- To control car audio module volume -->
- <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-
<!-- the ability to rename notifications posted by other apps -->
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
new file mode 100644
index 0000000..af6dd77
--- /dev/null
+++ b/packages/SystemUI/OWNERS
@@ -0,0 +1,20 @@
+set noparent
+
+dsandler@google.com
+
+asc@google.com
+ashaikh@google.com
+beverlyt@google.com
+cinek@google.com
+cwren@google.com
+evanlaird@google.com
+jmonk@google.com
+jaggies@google.com
+jjaggi@google.com
+juliacr@google.com
+dupin@google.com
+madym@google.com
+roosa@google.com
+shahrk@google.com
+winsonc@google.com
+
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index bf0d679..3f5f76f 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Volgende wekker gestel vir <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Vee uit"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Deaktiveer e-SIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Kan nie e-SIM deaktiveer nie"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Die e-SIM kan weens \'n fout nie gedeaktiveer word nie."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Het jy die patroon vergeet?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Verkeerde patroon"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Teken jou patroon"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Voer SIM se PIN in."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Voer SIM se PIN vir \"<xliff:g id="CARRIER">%1$s</xliff:g>\" in."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Deaktiveer e-SIM om toestel sonder mobiele diens te gebruik."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Deaktiveer e-SIM om toestel sonder mobiele diens te gebruik."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Voer PIN in"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Voer wagwoord in"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Kontak diensverskaffer vir besonderhede."</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 340e514..1cd862d 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"ቀጣዩ ማንቂያ ለ<xliff:g id="ALARM">%1$s</xliff:g> ተዘጋጅቷል"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ሰርዝ"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIMን አሰናክል"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"eSIMን ማሰናከል አልተቻለም"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"በአንድ ስህተት ምክንያት eSIM ሊሰናከል አልቻለም።"</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"አስገባ"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ስርዓተ ጥለቱን እርሳ"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"የተሳሳተ ስርዓተ ጥለት"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"ስርዓተ ጥለትዎን ይሳሉ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"የሲም ፒን ያስገቡ።"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"የ«<xliff:g id="CARRIER">%1$s</xliff:g>» ሲም ፒን ያስገቡ።"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"መሣሪያን ያለተንቀሳቃሽ ስልክ አገልግሎት ለመጠቀም eSIMን ያሰናክሉ።"</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> መሣሪያን ያለሞባይል አገልግሎት ለመጠቀም eSIMን ያሰናክሉ።"</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"ፒን ያስገቡ"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"የይለፍ ቃል ያስገቡ"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ሲም አሁን ተሰናክሏል። ለመቀጠል የፒዩኬ ኮድ ያስገቡ። ለዝርዝር አገልግሎት አቅራቢን ያግኙ።"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index b0503ea..3ab93c5 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Növbəti zəng vaxtı: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Silin"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSİM\'i deaktiv edin"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"eSIM-i deaktiv etmək alınmadı"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM xəta səbəbi ilə deaktiv edilmədi."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Daxil edin"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Modeli unutmuşam"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Yanlış Model"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Modeli çəkin"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM PIN\'ni daxil edin."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" üçün SIM PIN\'ni daxil edin."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Mobil xidmət olmadan cihazı istifadə etmək üçün eSIM\'i deaktiv edin."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Mobil xidmət olmadan cihazı istifadə etmək üçün eSIM-i deaktiv edin."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"PIN daxil edin"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Parol daxil edin"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM indi deaktivdir. Davam etmək üçün PUK kod daxil edin. Ətraflı məlumat üçün operatorla əlaqə saxlayın."</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 791f5e9..81c56da 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"পরবর্তী অ্যালার্ম <xliff:g id="ALARM">%1$s</xliff:g> এ সেট করা হয়েছে"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"মুছুন"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"ই-সিমটি অক্ষম করুন"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"এন্টার"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"প্যাটার্ন ভুলে গেছি"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ভুল প্যাটার্ন"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"আপনার প্যাটার্ন আঁকুন"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"সিমের পিন লিখুন।"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" এর জন্য সিমের পিন লিখুন।"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"মোবাইল পরিষেবা ছাড়াই ডিভাইস ব্যবহার করতে ই-সিম অক্ষম করুন।"</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"পিন লিখুন"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"পাসওয়ার্ড লিখুন"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"সিমটি এখন অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোডটি লিখুন। বিশদ বিবরণের জন্য পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 1f6addf..6ff819c 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Naredni alarm je podešen za <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Izbriši"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Onemogući eSIM karticu"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zaboravili ste uzorak?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Pogrešan uzorak"</string>
@@ -66,7 +70,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Nacrtajte uzorak"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Unesite PIN SIM kartice."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Unesite PIN SIM kartice operatera \"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Onemogućite eSIM karticu za korištenje uređaja bez mobilne usluge."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"Unesite PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Unesite lozinku"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM kartica je sada onemogućena. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index dd2844a..511516e 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"S\'ha definit la pròxima alarma per a l\'hora següent: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Suprimeix"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Desactiva l\'eSIM"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Retorn"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"He oblidat el patró"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"El patró no és correcte"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Dibuixa el patró"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Introdueix el PIN de la SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Introdueix el PIN de la SIM de: <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Desactiva l\'eSIM per utilitzar el dispositiu sense servei mòbil."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"Introdueix el PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Introdueix la contrasenya"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"La SIM està desactivada. Introdueix el codi PUK per continuar. Contacta amb l\'operador de telefonia mòbil per obtenir més informació."</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index cf7abf1..d5f80cb 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Další budík je nastaven na <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Smazat"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Deaktivovat eSIM kartu"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Nelze deaktivovat eSIM kartu"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM kartu kvůli chybě nelze deaktivovat."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zapomenuté gesto"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Nesprávné gesto"</string>
@@ -67,7 +69,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Zadejte gesto"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Zadejte kód PIN SIM karty."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Zadejte kód PIN SIM karty <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"eSIM kartu deaktivujte, chcete-li telefon používat bez mobilních služeb."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> eSIM kartu deaktivujte, chcete-li zařízení používat bez mobilních služeb."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Zadejte kód PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Zadejte heslo"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM karta byla zablokována. Chcete-li pokračovat, je třeba zadat kód PUK. Podrobné informace získáte od operátora."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index b6dc72b..57b98fa 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Next alarm set for <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Disable eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Can’t disable eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Draw your pattern"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Enter SIM PIN."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Enter SIM PIN for \'<xliff:g id="CARRIER">%1$s</xliff:g>\'."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Disable eSIM to use device without mobile service."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disable eSIM to use device without mobile service."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Enter PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Enter Password"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index b6dc72b..57b98fa 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Next alarm set for <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Disable eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Can’t disable eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Draw your pattern"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Enter SIM PIN."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Enter SIM PIN for \'<xliff:g id="CARRIER">%1$s</xliff:g>\'."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Disable eSIM to use device without mobile service."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disable eSIM to use device without mobile service."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Enter PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Enter Password"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index b6dc72b..57b98fa 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Next alarm set for <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Disable eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Can’t disable eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Draw your pattern"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Enter SIM PIN."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Enter SIM PIN for \'<xliff:g id="CARRIER">%1$s</xliff:g>\'."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Disable eSIM to use device without mobile service."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disable eSIM to use device without mobile service."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Enter PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Enter Password"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index b6dc72b..57b98fa 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Next alarm set for <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Disable eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Can’t disable eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Draw your pattern"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Enter SIM PIN."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Enter SIM PIN for \'<xliff:g id="CARRIER">%1$s</xliff:g>\'."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Disable eSIM to use device without mobile service."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disable eSIM to use device without mobile service."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Enter PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Enter Password"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 97d16c6..d27d204 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Next alarm set for <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Disable eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Can’t disable eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgot Pattern"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Draw your pattern"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Enter SIM PIN."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Enter SIM PIN for \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Disable eSIM to use device without mobile service."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disable eSIM to use device without mobile service."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Enter PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Enter Password"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM is now disabled. Enter PUK code to continue. Contact carrier for details."</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index ec36fd7..780d8d4 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Próxima alarma: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Eliminar"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Inhabilita la tarjeta eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"No se puede inhabilitar la tarjeta eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"No se puede mostrar la tarjeta eSIM debido a un error."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Intro"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"¿Has olvidado el patrón?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Patrón incorrecto"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Dibuja tu patrón"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Introduce el PIN de la tarjeta SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Introduce el PIN de la tarjeta SIM de <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Inhabilita la tarjeta eSIM para utilizar el dispositivo sin servicio móvil."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Inhabilita la tarjeta eSIM para usar el dispositivo sin servicio móvil."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Introduce el PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Introduce tu contraseña"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"La tarjeta SIM está inhabilitada. Para continuar, introduce el código PUK. Si quieres obtener más información, ponte en contacto con el operador."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 2b14e3c..44aab01 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Seuraava hälytys asetettu: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Poista"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Poista eSIM käytöstä"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"eSIMiä ei voi poistaa käytöstä"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Tapahtui virhe, eikä eSIMiä voitu poistaa käytöstä."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Unohtunut kuvio"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Väärä kuvio"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Piirrä kuvio"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Anna SIM-kortin PIN-koodi."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Anna operaattorin <xliff:g id="CARRIER">%1$s</xliff:g> SIM-kortin PIN-koodi."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Poista eSIM käytöstä, jos haluat käyttää laitetta ilman mobiilipalvelua."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Poista eSIM käytöstä, jos haluat käyttää laitetta ilman mobiililiittymää."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Anna PIN-koodi."</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Anna salasana"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM-kortti on nyt poistettu käytöstä. Jatka antamalla PUK-koodi. Saat lisätietoja ottamalla yhteyttä operaattoriin."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index b033311..e0b14b9 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Heure de la prochaine alarme : <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Supprimer"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Désactiver la carte eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Impossible de désactiver la carte eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"La carte eSIM ne peut pas être réinitialisée à cause d\'une erreur."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Entrée"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"J\'ai oublié le schéma"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Schéma incorrect"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Dessinez votre schéma"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Entrez le NIP de la carte SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Entrez le NIP de la carte SIM pour « <xliff:g id="CARRIER">%1$s</xliff:g> »."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Désactivez la carte eSIM pour utiliser l\'appareil sans service cellulaire."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Désactivez la carte eSIM pour utiliser l\'appareil sans service cellulaire."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Entrez le NIP"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Entrez votre mot de passe."</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Pour obtenir plus de détails, communiquez avec votre fournisseur de services."</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index c62bab8..d2e6c02 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"<xliff:g id="ALARM">%1$s</xliff:g> માટે આગલું એલાર્મ સેટ કર્યું"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"કાઢી નાખો"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIMને અક્ષમ કરો"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"દાખલ કરો"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"પૅટર્ન ભૂલી ગયાં"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ખોટી પૅટર્ન"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"તમારી પૅટર્ન દોરો"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"સિમ પિન દાખલ કરો"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" માટે સિમ પિન દાખલ કરો."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"મોબાઇલ સેવા વગર ઉપકરણનો ઉપયોગ કરવા eSIMને અક્ષમ કરો."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"પિન દાખલ કરો"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"પાસવર્ડ દાખલ કરો"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"સિમ હમણાં અક્ષમ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. વિગતો માટે કૅરિઅરનો સંપર્ક કરો."</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 641c072..dfd06b2 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"अगला अलार्म <xliff:g id="ALARM">%1$s</xliff:g> बजे के लिए सेट किया गया है"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"मिटाएं"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM अक्षम करें"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"पैटर्न भूल गए हैं"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"गलत पैटर्न"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"अपना पैटर्न बनाएं"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"सिम पिन डालें."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" के लिए सिम पिन डालें"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"मोबाइल सेवा के बिना डिवाइस का उपयोग करने के लिए eSIM अक्षम करें."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"पिन डालें"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"पासवर्ड डालें"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"सिम अब काम नहीं करेगा. जारी रखने के लिए PUK कोड डालें. ज़्यादा जानकारी के लिए अपनी मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें."</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 813377a..96bcb10 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Prossima sveglia impostata a: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Elimina"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Disattiva eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Impossibile disattivare la eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Impossibile disattivare la eSIM a causa di un errore."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Invio"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Sequenza dimenticata"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Sequenza sbagliata"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Inserisci la sequenza"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Inserisci il PIN della SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Inserisci il PIN della SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Disattiva l\'eSIM per usare il dispositivo senza servizio dati mobile."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disattiva la eSIM per usare il dispositivo senza servizio dati mobile."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Inserisci PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Inserisci la password"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"La scheda SIM è disattivata. Inserisci il codice PUK per continuare. Contatta l\'operatore per avere informazioni dettagliate."</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 2c29112..690ce65 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"<xliff:g id="ALARM">%1$s</xliff:g> ಗಂಟೆಗೆ ಮುಂದಿನ ಅಲಾರಮ್ ಹೊಂದಿಸಲಾಗಿದೆ"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ಅಳಿಸಿ"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ನಮೂದಿಸಿ"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ಪ್ಯಾಟರ್ನ್ ಮರೆತಿದ್ದೀರಿ"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ಪ್ಯಾಟರ್ನ್ ತಪ್ಪಾಗಿದೆ"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"ನಿಮ್ಮ ಪ್ಯಾಟರ್ನ್ ಚಿತ್ರಿಸಿ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" ಗಾಗಿ ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"ಮೊಬೈಲ್ ಸೇವೆ ಇಲ್ಲದೆಯೇ ಸಾಧನವನ್ನು ಬಳಸಲು eSIM ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"ಪಿನ್ ನಮೂದಿಸಿ"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"ಪಾಸ್ವರ್ಡ್ ನಮೂದಿಸಿ"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ಈಗ ಸಿಮ್ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ. ಮುಂದುವರೆಯಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 545d32a..10a81d0 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"ໂມງປຸກຕໍ່ໄປຖືກຕັ້ງໄວ້ເວລາ <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ລຶບ"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"ປິດການໃຊ້ eSIM ແລ້ວ"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ປ້ອນເຂົ້າ"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ລືມຮູບແບບປົດລັອກ?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ຮູບແບບຜິດ"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"ແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"ໃສ່ລະຫັດ PIN ຂອງຊິມ."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"ໃສ່ລະຫັດ PIN ຂອງຊິມສຳລັບ \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"ປິດການໃຊ້ eSIM ເພື່ອໃຊ້ອຸປະກອນໂດຍບໍ່ມີບໍລິການມືຖື."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"ໃສ່ລະຫັດ PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"ໃສ່ລະຫັດຜ່ານ"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ຊິມຖືກປິດການນຳໃຊ້ແລ້ວ. ປ້ອນລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ຕິດຕໍ່ຜູ່ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 0951787..d060ff4 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Следниот аларм е поставен во <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Избриши"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Оневозможи ја eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Не може да се оневозможи eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM-картичката не може да се оневозможи поради грешка."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Внеси"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ја заборавивте шемата?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Погрешна шема"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Нацртајте ја шемата"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Внесете PIN на SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Внесете PIN на SIM за „<xliff:g id="CARRIER">%1$s</xliff:g>“."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Оневозможете ја eSIM за да го користите уредот без мобилна услуга."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Оневозможете ја eSIM-картичката за да го користите уредот без мобилна услуга."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Внесете PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Внесете лозинка"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Контактирајте со операторот за детали."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index d62537d..7e12a22 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"അടുത്ത അലാറം <xliff:g id="ALARM">%1$s</xliff:g>-ന് സജ്ജീകരിച്ചു"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ഇല്ലാതാക്കുക"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM പ്രവർത്തനരഹിതമാക്കുക"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"എന്റർ"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"പാറ്റേൺ മറന്നു"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"പാറ്റേൺ തെറ്റാണ്"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"നിങ്ങളുടെ പാറ്റേൺ വരയ്ക്കുക"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"സിം പിൻ നൽകുക."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" എന്ന കാരിയർക്കുള്ള സിം പിൻ നൽകുക."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"മൊബൈൽ സേവനമില്ലാതെ ഉപകരണം ഉപയോഗിക്കാൻ eSIM പ്രവർത്തനരഹിതമാക്കുക."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"പിൻ നൽകുക"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"പാസ്വേഡ് നൽകുക"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. വിശദാംശങ്ങൾക്ക് കാരിയറെ ബന്ധപ്പെടുക."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 7d04edc..daa40c6 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"पुढील अलार्म <xliff:g id="ALARM">%1$s</xliff:g> साठी सेट केला"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"हटवा"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM बंद करा"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"एंटर करा"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"पॅटर्न विसरलात"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"चुकीचा पॅटर्न"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"तुमचा पॅटर्न काढा"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"सिम पिन एंटर करा"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" साठी सिम पिन एंटर करा"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"मोबाइल सेवांशिवाय डिव्हाइस वापरण्यासाठी eSIM बंद करा."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"पिन एंटर करा"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"पासवर्ड एंटर करा"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"सिम आता अक्षम केले आहे. सुरू ठेवण्यासाठी PUK कोड एंटर करा. तपशीलांसाठी वाहकाशी संपर्क साधा."</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index fb85589..a7236cf 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"နောက်နှိုးစက်အချိန်ကို <xliff:g id="ALARM">%1$s</xliff:g> တွင် သတ်မှတ်ထားပါသည်"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ဖျက်ရန်"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM ကို ပိတ်ရန်"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"eSIM ကို ပိတ်၍မရခြင်း"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"အမှားအယွင်းရှိနေသောကြောင့် eSIM ကို ပိတ်၍မရပါ။"</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter ခလုတ်"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ပုံစံအား မေ့သွားပါသည်"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ပုံစံ မမှန်ကန်ပါ"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"ပုံစံကို ဆွဲပါ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"ဆင်းမ်ကဒ် ပင်နံပါတ်ကို ထည့်ပါ။"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" အတွက် ဆင်းမ်ကဒ်ပင်နံပါတ်ကို ထည့်ပါ။"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"စက်ပစ္စည်းကို မိုဘိုင်းဝန်ဆောင်မှုမပါဘဲ အသုံးပြုရန် eSIM ကို ပိတ်ပါ။"</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> မိုဘိုင်းဝန်ဆောင်မှု မရှိဘဲ စက်ပစ္စည်းကို အသုံးပြုရန် eSIM ကို ပိတ်ပါ။"</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"ပင်နံပါတ်ကို ထည့်ပါ"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"စကားဝှက်ကို ထည့်ပါ"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် ပင်နံပါတ် ပြန်ဖွင့်သည့် ကုဒ်ကို ထည့်ပါ။ ပိုမိုလေ့လာရန် မိုဘိုင်းဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်နိုင်ပါသည်။"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index ca51c96..2c0e3d6 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Neste alarm er stilt inn for <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Slett"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Deaktiver e-SIM-kortet"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Har du glemt mønsteret?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Feil mønster"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Tegn mønsteret ditt"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Skriv inn PIN-koden for SIM-kortet."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Skriv inn PIN-koden for SIM-kortet «<xliff:g id="CARRIER">%1$s</xliff:g>»."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Deaktiver e-SIM-kortet for å bruke enheten uten mobiltjeneste."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"Skriv inn PIN-koden"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Skriv inn passordet"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM-kortet er nå deaktivert. Skriv inn PUK-koden for å fortsette. Ta kontakt med operatøren for mer informasjon."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 90d6c5cfc..dcb309f 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"अर्को अलार्म <xliff:g id="ALARM">%1$s</xliff:g> का लागि सेट गरियो"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"मेट्नुहोस्"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM लाई असक्षम पार्नुहोस्"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"प्रविष्टि गर्नुहोस्"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ढाँचा बिर्सनुभयो"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"गलत ढाँचा"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"आफ्नो ढाँचा कोर्नुहोस्"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM को PIN प्रविष्टि गर्नुहोस्।"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" को SIM को PIN प्रविष्ट गर्नुहोस्।"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"मोबाइल सेवा बिना यन्त्रको प्रयोग गर्न eSIM लाई असक्षम पार्नुहोस्।"</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"PIN प्रविष्टि गर्नुहोस्"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"पासवर्ड प्रविष्ट गर्नुहोस्"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्ट गर्नुहोस्। विवरणको लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index d5d27ca..ddb83e9 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"ਅਗਲਾ ਅਲਾਰਮ <xliff:g id="ALARM">%1$s</xliff:g> \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"ਮਿਟਾਓ"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM ਨੂੰ ਅਯੋਗ ਬਣਾਓ"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ਦਾਖਲ ਕਰੋ"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ਪੈਟਰਨ ਭੁੱਲ ਗਏ"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ਗਲਤ ਪੈਟਰਨ"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"ਆਪਣਾ ਪੈਟਰਨ ਉਲੀਕੋ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" ਲਈ ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"ਮੋਬਾਈਲ ਸੇਵਾ ਤੋਂ ਬਿਨਾਂ ਡੀਵਾਈਸ ਨੂੰ ਵਰਤਣ ਲਈ eSIM ਬੰਦ ਕਰੋ।"</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"ਪਿੰਨ ਦਾਖਲ ਕਰੋ"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ਸਿਮ ਹੁਣ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index bb547f15..5c02d4f 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Próximo alarme definido para <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Excluir"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Desativar eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Não é possível desativar o eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Não é possível desativar o eSIM devido a um erro."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Inserir"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueci o padrão"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrão incorreto"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe seu padrão"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Informe o PIN do cartão SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Informe o PIN do cartão SIM para \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Desative o eSIM para usar o dispositivo sem serviço móvel."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desative o eSIM para usar o dispositivo sem serviço móvel."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Digite o PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Digite a senha"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para mais detalhes."</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 6a0e627..73f2b7f 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Próximo alarme definido para <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Eliminar"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Desativar eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Não é possível desativar o eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Não é possível desativar o eSIM devido a um erro."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Tecla Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueceu-se do padrão"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrão incorreto"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe o seu padrão"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Introduza o PIN do cartão SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Introduza o PIN do cartão SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Desativar eSIM para utilizar sem serviço móvel"</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desative o eSIM para utilizar o dispositivo sem serviço móvel."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Introduza o PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Introduza a palavra-passe"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O cartão SIM está agora desativado. Introduza o código PUK para continuar. Contacte o operador para obter mais detalhes."</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index bb547f15..5c02d4f 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Próximo alarme definido para <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Excluir"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Desativar eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Não é possível desativar o eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Não é possível desativar o eSIM devido a um erro."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Inserir"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueci o padrão"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrão incorreto"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe seu padrão"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Informe o PIN do cartão SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Informe o PIN do cartão SIM para \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Desative o eSIM para usar o dispositivo sem serviço móvel."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desative o eSIM para usar o dispositivo sem serviço móvel."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Digite o PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Digite a senha"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para mais detalhes."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 341c952..f449701 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Următoarea alarmă este setată pentru <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Ștergeți"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Dezactivați cardul eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Nu se poate dezactiva cardul eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Cardul eSIM nu poate fi dezactivat din cauza unei erori."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Introduceți"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ați uitat modelul"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Model greșit"</string>
@@ -66,7 +68,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenați modelul"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Introduceți codul PIN al cardului SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Introduceți codul PIN al cardului SIM pentru „<xliff:g id="CARRIER">%1$s</xliff:g>”."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Dezactivați cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivați cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Introduceți codul PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Introduceți parola"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"Cardul SIM este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index 1a794ee..dbdbf93 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Время следующего сигнала будильника: <xliff:g id="ALARM">%1$s</xliff:g>."</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Удалить"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Отключить eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Произошла ошибка"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Не удалось отключить eSIM."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Клавиша ввода"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Забыли графический ключ?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Неверный графический ключ"</string>
@@ -67,7 +69,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Введите графический ключ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Введите PIN-код SIM-карты."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Введите PIN-код SIM-карты \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Чтобы пользоваться устройством без мобильной связи, отключите eSIM."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Чтобы пользоваться устройством без мобильной связи, отключите eSIM."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Введите PIN-код"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Введите пароль"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM-карта отключена. Чтобы продолжить, введите PUK-код. За подробной информацией обратитесь к оператору связи."</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index d36e752..a670f16 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"<xliff:g id="ALARM">%1$s</xliff:g>ට ඊළඟ එලාමය සකසා ඇත"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"මකන්න"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM අබල කරන්න"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"eSIM අබල කළ නොහැකිය."</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"දෝෂයක් හේතුවෙන් eSIM අබල කළ නොහැකිය."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ඇතුල් කරන්න"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"රටාව අමතකයි"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"වැරදි රටාවකි"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"ඔබගේ රටාව අඳින්න"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM PIN ඇතුළු කරන්න"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" සඳහා SIM PIN ඇතුළු කරන්න"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"ජංගම සේවාවෙන් තොරව උපාංගය භාවිත කිරීමට eSIM අබල කරන්න."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> ජංගම සේවාවෙන් තොරව උපාංගය භාවිත කිරීමට eSIM අබල කරන්න."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"PIN එක ඇතුළු කරන්න"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"මුරපදය ඇතුළු කරන්න"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"දැන් SIM එක අබල කර ඇත. ඉදිරියට යාමට PUK කේතය යොදන්න. විස්තර සඳහා වාහකයා අමතන්න."</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 7e85131..06acf81 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Kengele inayofuata italia saa <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Futa"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Zima eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Imeshindwa kuzima eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Hitilafu imetokea wakati wa kuzima eSIM."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Weka"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Umesahau Mchoro"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Mchoro si Sahihi"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Chora mchoro wako"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Weka PIN ya SIM."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Weka PIN ya SIM ya \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Zima eSIM ili utumie kifaa bila huduma ya vifaa vya mkononi."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Zima eSIM ili utumie kifaa bila huduma ya vifaa vya mkononi."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Weka PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Weka Nenosiri"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM sasa imefungwa. Weka nambari ya PUK ili uendelee. Wasiliana na mtoa huduma za mtandao kwa maelezo."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 2ce57a0..376c74f 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"அடுத்த அலாரம் <xliff:g id="ALARM">%1$s</xliff:g>க்கு அமைக்கப்பட்டுள்ளது"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"நீக்கும் பொத்தான்"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"இ-சிம்மை முடக்கும்"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"என்டர் பொத்தான்"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"பேட்டர்ன் நினைவில்லையா"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"தவறான பேட்டர்ன்"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"பேட்டர்னை வரையவும்"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"சிம் பின்னை உள்ளிடவும்."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\"க்கான சிம் பின்னை உள்ளிடவும்."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"மொபைல் சேவை இல்லாமல் சாதனத்தைப் பயன்படுத்த, இ-சிம்மை முடக்கவும்."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"பின்னை உள்ளிடவும்"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"கடவுச்சொல்லை உள்ளிடவும்"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர, PUK குறியீட்டை உள்ளிடவும். விவரங்களுக்கு, தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்புகொள்ளவும்."</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index bdee4a3..934d6a2 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"తర్వాత అలారం <xliff:g id="ALARM">%1$s</xliff:g>కి సెట్ చేయబడింది"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"తొలగించు"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIMని నిలిపివేయండి"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"నమూనాను మర్చిపోయాను"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"ఆకృతి తప్పు"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"మీ నమూనాను గీయండి"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM పిన్ని నమోదు చేయండి."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" కోసం SIM పిన్ని నమోదు చేయండి."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"మొబైల్ సేవ లేకుండా డివైజ్ని ఉపయోగించడం కోసం eSIMని నిలిపివేయండి."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"పిన్ను నమోదు చేయండి"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"పాస్వర్డ్ని నమోదు చేయండి"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"ఇప్పుడు SIM నిలిపివేయబడింది. కొనసాగించాలంటే, PUK కోడ్ను నమోదు చేయండి. వివరాల కోసం క్యారియర్ను సంప్రదించండి."</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index ca53dfb8..e716ee4 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Sonraki alarm <xliff:g id="ALARM">%1$s</xliff:g> olarak ayarlandı"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM\'i devre dışı bırak"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Deseni unuttunuz mu?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Yanlış Desen"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Deseninizi çizin"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM PIN kodunu girin."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" için SIM PIN kodunu girin."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Cihazı mobil hizmet olmadan kullanmak için eSIM\'i devre dışı bırakın."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"PIN\'i girin"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Şifreyi Girin"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM kart artık devre dışı bırakıldı. Devam etmek için PUK kodunu girin. Ayrıntılı bilgi için operatörle bağlantı kurun."</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 4b79744..277b103 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Наступний сигнал: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Видалити"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Вимкнути eSIM-карту"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Не вдається вимкнути eSIM-карту"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"Не вдається вимкнути eSIM-карту через помилку."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Ввести"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Не пам’ятаю ключ"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Неправильний ключ"</string>
@@ -67,7 +69,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Намалюйте ключ"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Введіть PIN-код SIM-карти."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Введіть PIN-код SIM-карти для оператора \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Вимкнути eSIM-карту, щоб використовувати пристрій без мобільного зв’язку."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Вимкніть eSIM-карту, щоб використовувати пристрій без мобільного зв’язку."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Введіть PIN-код"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Введіть пароль"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"Зараз SIM-карту вимкнено. Введіть PUK-код, щоб продовжити. Зв’яжіться з оператором, щоб дізнатися більше."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index cd99c92..28a52c0 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"اگلا الارم <xliff:g id="ALARM">%1$s</xliff:g> کیلئے سیٹ ہے"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"حذف کریں"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIM غیر فعال کریں"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"درج کریں"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"پیٹرن بھول گئے"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"غلط پیٹرن"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"اپنا پیٹرن ڈرا کریں"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM PIN درج کریں۔"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" کیلئے SIM PIN درج کریں۔"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"موبائل سروس کے بغیر آلہ کا استعمال کرنے کیلئے eSIM غیر فعال کریں۔"</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"PIN درج کریں"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"پاسورڈ درج کریں"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM اب غیر فعال ہوگیا ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 204bb8b..3272cb2 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Signal <xliff:g id="ALARM">%1$s</xliff:g> da chalinadi."</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"O‘chirib tashlash"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"eSIMni faolsizlantirish"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter tugmasi"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Grafik kalit esimdan chiqdi"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Grafik kalit xato"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Grafik kalit chizing"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"SIM karta PIN kodini kiriting."</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"“<xliff:g id="CARRIER">%1$s</xliff:g>” SIM kartasi PIN kodini kiriting."</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Qurilmadan mobil xizmatlarsiz foydalanish uchun eSIMni faolsizlantiring."</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"PIN kodni kiriting"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Parol kiriting"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM karta hozir o‘chirilgan. Davom etish uchun PUK kodni kiriting. Batafsil axborot olish uchun tarmoq operatori bilan bog‘laning."</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 45ab593..ee234cc 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -53,6 +53,10 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"已設定下一個鬧鐘時間:<xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"刪除"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"停用 eSIM 卡"</string>
+ <!-- no translation found for error_disable_esim_title (4852978431156228006) -->
+ <skip />
+ <!-- no translation found for error_disable_esim_msg (676694908770135639) -->
+ <skip />
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter 鍵"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"忘記解鎖圖案"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"解鎖圖案錯誤"</string>
@@ -65,7 +69,8 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"畫出解鎖圖案"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"輸入 SIM 卡的 PIN 碼。"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"輸入「<xliff:g id="CARRIER">%1$s</xliff:g>」SIM 卡的 PIN 碼。"</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"停用 eSIM 卡即可在沒有行動服務的情況下使用裝置。"</string>
+ <!-- no translation found for kg_sim_lock_esim_instructions (4416732549172148542) -->
+ <skip />
<string name="kg_pin_instructions" msgid="4069609316644030034">"輸入 PIN 碼"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"輸入密碼"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM 卡已遭停用,輸入 PUK 碼即可繼續使用。如需瞭解詳情,請與電信業者聯絡。"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 2329320..2415bf3 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -53,6 +53,8 @@
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"I-alamu elandelayo esethelwe i-<xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Susa"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Khubaza i-eSIM"</string>
+ <string name="error_disable_esim_title" msgid="4852978431156228006">"Ayikwazi ukukhubaz i-eSIM"</string>
+ <string name="error_disable_esim_msg" msgid="676694908770135639">"I-eSIM ayikwakhi ukukhutshazwa ngenxa yephutha."</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Faka"</string>
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ukhohlwe iphethini?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Iphatheni engalungile"</string>
@@ -65,7 +67,7 @@
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Dweba iphethini yakho"</string>
<string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Faka i-PIN ye-SIM"</string>
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Faka i-PIN ye-SIM ye-\"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string>
- <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Khubaza i-eSIM ukusebenzisa idivayisi ngaphandle kwesevisi yeselula."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Khubaza i-eSIM ukuze usebenzise le sevisi yeselula."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Faka iphinikhodi"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Faka iphasiwedi"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Xhumana nenkampani yenethiwekhi ngemininingwane."</string>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 997fe6d..97472a4 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -20,21 +20,33 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/qs_footer"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/qs_footer_height"
android:elevation="4dp"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingTop="0dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
+ android:clipToPadding="false">
+
+ <View
+ android:id="@+id/qs_footer_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/qs_background_primary" />
+
+ <View
+ android:id="@+id/qs_footer_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_gravity="top"
+ android:background="?android:attr/dividerHorizontal"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_marginTop="1dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
+ android:layout_gravity="center_vertical"
android:gravity="end" >
<LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1dab761..15b0f00 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -54,7 +54,6 @@
android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="48dp"
android:elevation="4dp"
/>
diff --git a/packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
similarity index 96%
rename from packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml
rename to packages/SystemUI/res/layout/recents_onboarding.xml
index b3d5c90..12f278a 100644
--- a/packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -30,7 +30,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:text="@string/recents_swipe_up_onboarding"
android:textColor="@android:color/white"
android:textSize="16sp"
android:drawableBottom="@drawable/ic_chevron_up"/>
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index b5ef1d7..61fe906 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -78,7 +78,25 @@
android:id="@+id/screen_pinning_home_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:paddingLeft="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index f3a6d44..d1ca2ce 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -80,7 +80,27 @@
android:id="@+id/screen_pinning_home_group"
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
- android:layout_weight="0" >
+ android:layout_weight="0"
+ android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg_light"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:layout_marginLeft="@dimen/screen_pinning_request_seascape_padding_negative"
+ android:src="@drawable/screen_pinning_light_bg_circ" />
+
+ <ImageView
+ android:id="@+id/screen_pinning_home_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="matrix"
+ android:layout_marginLeft="@dimen/screen_pinning_request_seascape_button_offset"
+ android:paddingRight="@dimen/screen_pinning_request_inner_padding"
+ android:paddingTop="@dimen/screen_pinning_request_inner_padding"
+ android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
+ android:src="@drawable/screen_pinning_bg_circ" />
<ImageView
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 53dff05..36298ca 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -20,14 +20,15 @@
android:background="@android:color/transparent"
android:theme="@style/qs_theme"
android:clipChildren="false" >
+ <!-- right-aligned to be physically near volume button -->
<LinearLayout
android:id="@+id/volume_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
+ android:layout_gravity="center_vertical|right"
android:minWidth="@dimen/volume_dialog_panel_width"
android:background="@android:color/transparent"
- android:layout_margin="12dp"
+ android:layout_margin="@dimen/volume_dialog_base_margin"
android:translationZ="8dp"
android:orientation="vertical"
android:clipChildren="false" >
@@ -38,8 +39,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp"
android:background="@drawable/rounded_bg_full"
android:translationZ="8dp"
android:orientation="horizontal" >
@@ -71,7 +72,7 @@
android:maxLines="1"
android:layout_centerVertical="true"
android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="@style/TextAppearance.Volume.Header" />
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/ringer_icon"
@@ -89,7 +90,7 @@
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index a9e5adf..70654a8 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -29,15 +29,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:padding="10dp">
+ android:padding="5dp">
<TextView
android:id="@+id/volume_row_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
+ android:maxLength="10"
android:maxLines="1"
android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:textAppearance="@style/TextAppearance.Volume.Header" />
<LinearLayout
android:id="@+id/output_chooser"
android:orientation="vertical"
@@ -53,9 +54,10 @@
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:maxLength="10"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
+ android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/output_chooser_button"
android:layout_width="24dp"
@@ -73,15 +75,18 @@
android:id="@+id/volume_row_slider_frame"
android:padding="0dp"
android:layout_width="@dimen/volume_dialog_panel_width"
- android:layout_height="150dp">
+ android:layoutDirection="rtl"
+ android:layout_height="@dimen/volume_dialog_panel_width">
<SeekBar
android:id="@+id/volume_row_slider"
+ android:clickable="true"
android:padding="0dp"
android:layout_margin="0dp"
- android:layout_width="150dp"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layoutDirection="rtl"
android:layout_gravity="center"
- android:rotation="270" />
+ android:rotation="90" />
</FrameLayout>
<com.android.keyguard.AlphaOptimizedImageButton
diff --git a/packages/SystemUI/res/values-af/config.xml b/packages/SystemUI/res/values-af/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-af/config.xml
+++ b/packages/SystemUI/res/values-af/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 5242029..9d090ba7 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"maak kamera oop"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Kies nuwe taakuitleg"</string>
<string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukikoon"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Programikoon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Hulpboodskapgebied"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth gekoppel."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi af"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi is aan"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Geen Wi-Fi-netwerke beskikbaar nie"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Saai uit"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Saai tans uit"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Onbenoemde toestel"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Koppel tans …"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"USB-verbinding"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Warmkol"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Skakel tans aan …"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d toestelle</item>
+ <item quantity="one">%d toestel</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Kennisgewings"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Flitslig"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiele data"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> gebruik"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g>-limiet"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Werkprofiel"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Kennisgewings en programme is af"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Aandbeligting"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Aan by sonsondergang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Tot sonsopkoms"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is in veiligmodus gedeaktiveer."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vee alles uit"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Trek hier om verdeelde skerm te gebruik"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swiep op om programme te wissel"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Verdeel horisontaal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verdeel vertikaal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Verdeel gepasmaak"</string>
@@ -502,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Skakel nou af"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Vou uit"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Vou in"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Wissel uitvoertoestel"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skerm is vasgespeld"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Oorsig om dit te ontspeld."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Oorsig om dit te ontspeld."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Het dit"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nee, dankie"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Versteek <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Dit sal verskyn die volgende keer wanneer jy dit in instellings aanskakel."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Versteek"</string>
@@ -586,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Met kragkennisgewingkontroles kan jy \'n belangrikheidvlak van 0 tot 5 vir \'n program se kennisgewings stel. \n\n"<b>"Vlak 5"</b>" \n- Wys aan die bokant van die kennisgewinglys \n- Laat volskermonderbreking toe \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 4"</b>" \n- Verhoed volskermonderbreking \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 3"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n\n"<b>"Vlak 2"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n\n"<b>"Vlak 1"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n- Versteek van sluitskerm en statusbalk \n- Wys aan die onderkant van die kennisgewinglys \n\n"<b>"Vlak 0"</b>" \n- Blokkeer alle kennisgewings van die program af"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Kennisgewings"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Jy sal nie meer hierdie kennisgewings sien nie"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Jy maak hierdie kennisgewings gewoonlik toe. \nMoet ons aanhou om hulle te wys?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Hou aan om hierdie kennisgewings te wys?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop kennisgewings"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Hou aan wys"</string>
diff --git a/packages/SystemUI/res/values-am/config.xml b/packages/SystemUI/res/values-am/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-am/config.xml
+++ b/packages/SystemUI/res/values-am/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f08c71e..cf1adf2 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ካሜራ ክፈት"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"የአዲስ ተግባር አቀማመጥን ይምረጡ"</string>
<string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"የጣት አሻራ አዶ"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"የመተግበሪያ አዶ"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"የእገዛ መልዕክት አካባቢ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ብሉቱዝ ተያይዟል።"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ጠፍቷል"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi በርቷል"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"ምንም የWi-Fi አውታረ መረቦች የሉም"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"በመውሰድ ላይ"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ያልተሰየመ መሳሪያ"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"በማገናኘት ላይ..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"በማገናኘት ላይ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"መገናኛ ነጥብ"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"በማብራት ላይ..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d መሣሪያዎች</item>
+ <item quantity="other">%d መሣሪያዎች</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ማሳወቂያዎች"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"የባትሪ ብርሃን"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ጥቅም ላይ ውሏል"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ገደብ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"የ<xliff:g id="DATA_LIMIT">%s</xliff:g> ማስጠንቀቂያ"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"የሥራ መገለጫ"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"ማሳወቂያዎች እና መተግበሪያዎች ጠፍተዋል"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"የምሽት ብርሃን"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"ጸሐይ ስትጠልቅ ይበራል"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"ጸሐይ እስክትወጣ ድረስ"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> በጥንቃቄ ሁነታ ውስጥ ታግዷል።"</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"ሁሉንም አጽዳ"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"የተከፈለ ማያ ገጽን ለመጠቀም እዚህ ላይ ይጎትቱ"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"መተግበሪያዎችን ለመቀየር ወደ ላይ ያንሸራትቱ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"አግድም ክፈል"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ቁልቁል ክፈል"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"በብጁ ክፈል"</string>
@@ -502,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"አሁን አጥፋ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"አስፋ"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ሰብስብ"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"የውጽዓት መሣሪያን ይቀይሩ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"ማያ ገጽ ተሰክቷል"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"ይሄ እስኪነቅሉት ድረስ በእይታ ውስጥ ያስቀምጠዋል። ለመንቀል ተመለስ እና አጠቃላይ ዕይታ የሚለውን ይጫኑ እና ይያዙ።"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"ይሄ እስኪነቅሉት ድረስ በእይታ ውስጥ ያስቀምጠዋል። ለመንቀል አጠቃላይ ዕይታ ተጭነው ይያዙ።"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ገባኝ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"አይ፣ አመሰግናለሁ"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ይደበቅ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"በቅንብሮች ውስጥ በሚቀጥለው ጊዜ እንዲበራ በሚያደርጉበት ጊዜ ዳግመኛ ብቅ ይላል።"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ደብቅ"</string>
@@ -586,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"በኃይል ማሳወቂያ መቆጣጠሪያዎች አማካኝነት የአንድ መተግበሪያ ማሳወቂያዎች የአስፈላጊነት ደረጃ ከ0 እስከ 5 ድረስ ማዘጋጀት ይችላሉ። \n\n"<b>"ደረጃ 5"</b>" \n- በማሳወቂያ ዝርዝሩ አናት ላይ አሳይ \n- የሙሉ ማያ ገጽ ማቋረጥን ፍቀድ \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 4"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 3"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- በፍጹም አጮልቀው አይምልከቱ \n\n"<b>"ደረጃ 2"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ እና ንዝረትን በፍጹም አይኑር \n\n"<b>"ደረጃ 1"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ ወይም ንዝረትን በፍጹም አያደርጉ \n- ከመቆለፊያ ገጽ እና የሁኔታ አሞሌ ይደብቁ \n- በማሳወቂያ ዝርዝር ግርጌ ላይ አሳይ \n\n"<b>"ደረጃ 0"</b>" \n- ሁሉንም የመተግበሪያው ማሳወቂያዎች ያግዱ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ማሳወቂያዎች"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"እነዚህን ማሳወቂያዎችን ከእንግዲህ አይመለከቷቸውም"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"አብዛኛውን ጊዜ እነዚህን ማሳወቂያዎች ያሰናብቷቸዋል። \nመታየታቸው ይቀጥል??"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"እነዚህን ማሳወቂያዎች ማሳየት ይቀጥሉ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ማሳወቂያዎችን አስቁም"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ማሳየትን ቀጥል"</string>
diff --git a/packages/SystemUI/res/values-ar/config.xml b/packages/SystemUI/res/values-ar/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ar/config.xml
+++ b/packages/SystemUI/res/values-ar/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-az/config.xml b/packages/SystemUI/res/values-az/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-az/config.xml
+++ b/packages/SystemUI/res/values-az/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 17d7718..e00a2dd 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"kemaranı açın"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Yeni tapşırıq sxemi seçin"</string>
<string name="cancel" msgid="6442560571259935130">"Ləğv et"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmaq izi ikonası"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Tətbiq ikonası"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Yardım mesajı bölməsi"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyğunluq zoom düyməsi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha böyük ekranda uzaqlaşdır."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth qoşulub."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi sönülüdür"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi Aktiv"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Heç bir Wi-Fi şəbəkəsi əlçatan deyil"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Yayım"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Yayım"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Adsız cihaz"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Qoşulur..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Birləşmə"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Aktiv edilir..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d cihaz</item>
+ <item quantity="one">%d cihaz</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirişlər"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"İşartı"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobil data"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"İndi deaktiv edin"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Genişləndirin"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Yığcamlaşdırın"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Çıxış cihazına keçin"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekrana sancaq taxıldı"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə basıb saxlayın."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri düyməsinə basıb saxlayın."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Anladım!"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Yox, çox sağ olun"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlədilsin?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ayarlarda onu aktivləşdirəcəyiniz vaxta qədər o, yenidən görünəcək."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Gizlədin"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Enerji bildiriş nəzarəti ilə, tətbiq bildirişləri üçün əhəmiyyət səviyyəsini 0-dan 5-ə kimi ayarlaya bilərsiniz. \n\n"<b>"Səviyyə 5"</b>" \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n- Tam ekran kəsintisinə icazə verin \n- Hər zaman izləyin \n\n"<b>"Səviyyə 4"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Hər zaman izləyin \n\n"<b>"Level 3"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n\n"<b>"Level 2"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n\n"<b>"Səviyyə 1"</b>" \n- Prevent full screen interruption \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n- Ekran kilidi və ya status panelindən gizlədin \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n\n"<b>"Səviyyə 0"</b>" \n- Bütün bildirişləri tətbiqdən blok edin"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirişlər"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Artıq bu bildirişləri görməyəcəkəsiniz"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Adətən bu bildirişləri rədd edirsiniz. \nBildirişlər göstərilsin?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Bu bildirişlər göstərilməyə davam edilsin?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bildirişləri dayandırın"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Göstərməyə davam edin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/config.xml b/packages/SystemUI/res/values-b+sr+Latn/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/config.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-be/config.xml b/packages/SystemUI/res/values-be/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-be/config.xml
+++ b/packages/SystemUI/res/values-be/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/config.xml b/packages/SystemUI/res/values-bg/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-bg/config.xml
+++ b/packages/SystemUI/res/values-bg/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/config.xml b/packages/SystemUI/res/values-bn/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-bn/config.xml
+++ b/packages/SystemUI/res/values-bn/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 4cbb5ed..eb08f50 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ক্যামেরা খুলুন"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কার্য লেআউট বেছে নিন"</string>
<string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"আঙ্গুলের ছাপের আইকন"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"অ্যাপ্লিকেশনের আইকন"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"সহায়তার মেসেজ দেখানোর জায়গা"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"সামঞ্জস্যের জুম বোতাম৷"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ছোট থেকে বৃহৎ স্ক্রীণে জুম করুন৷"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ব্লুটুথ সংযুক্ত হয়েছে৷"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ওয়াই-ফাই বন্ধ"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"ওয়াই-ফাই চালু আছে"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"কোনো ওয়াই-ফাই নেটওয়ার্ক উপলব্ধ নেই"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"কাস্ট করুন"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"কাস্ট করা হচ্ছে"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"নামবিহীন ডিভাইস"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"সংযুক্ত হচ্ছে..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"টেদারিং"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"হটস্পট"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"চালু করা হচ্ছে…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%dটি ডিভাইস</item>
+ <item quantity="other">%dটি ডিভাইস</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"বিজ্ঞপ্তিগুলি"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ফ্ল্যাশলাইট"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"মোবাইল ডেটা"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ব্যবহৃত হয়েছে"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"সীমা <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সতর্কতা"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"কাজের প্রোফাইল"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"বিজ্ঞপ্তি ও অ্যাপ বন্ধ আছে"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"নাইট লাইট"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"সূর্যাস্তে চালু হবে"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"সূর্যোদয় পর্যন্ত"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"নিরাপদ মোডে <xliff:g id="APP">%s</xliff:g> অক্ষম করা হয়েছে৷"</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"সবকিছু সাফ করুন"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"বিভক্ত স্ক্রীন ব্যবহার করতে এখানে টেনে আনুন"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"অন্য অ্যাপে যেতে উপরের দিকে সোয়াইপ করুন"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"অনুভূমিক স্প্লিট"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উল্লম্ব স্প্লিট"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"কাস্টম স্প্লিট করুন"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"এখনই বন্ধ করুন"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"বড় করুন"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"সঙ্কুচিত করুন"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"স্ক্রিন পিন করা হয়েছে"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"এটি আপনি আনপিন না করা পর্যন্ত এটিকে প্রদর্শিত করবে৷ আনপিন করতে ফিরুন এবং ওভারভিউ স্পর্শ করে ধরে থাকুন।"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"এটি আপনি আনপিন না করা পর্যন্ত এটিকে প্রদর্শিত করবে৷ আনপিন করতে ওভারভিউ স্পর্শ করে ধরে থাকুন৷"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"বুঝেছি"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"না থাক"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকাবেন?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"আপনি পরের বার সেটিংস-এ এটি চালু করলে এটি উপস্থিত হবে"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"লুকান"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"পাওয়ার বিজ্ঞপ্তির নিয়ন্ত্রণগুলি ব্যহবার করে, আপনি কোনও অ্যাপ্লিকেশনের বিজ্ঞপ্তির জন্য ০ থেকে ৫ পর্যন্ত একটি গুরুত্বের লেভেলকে সেট করতে পারবেন৷ \n\n"<b>"লেভেল ৫"</b>" \n- বিজ্ঞপ্তি তালিকার শীর্ষে দেখায় \n- পূর্ণ স্ক্রিনের বাধাকে অনুমতি দেয় \n- সর্বদা স্ক্রিনে উপস্থিত হয় \n\n"<b>"লেভেল ৪"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- সর্বদা স্ক্রিনে উপস্থিত হয় \n\n"<b>"লেভেল ৩"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n\n"<b>"লেভেল ২"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n- কখনওই শব্দ এবং কম্পন করে না \n\n"<b>"লেভেল ১"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n- কখনওই শব্দ এবং কম্পন করে না \n- লক স্ক্রিন এবং স্ট্যাটাস বার থেকে লুকায় \n- বিজ্ঞপ্তি তালিকার নীচের দিকে দেখায় \n\n"<b>"লেভেল ০"</b>" \n- অ্যাপ্লিকেশন থেকে সমস্ত বিজ্ঞপ্তিকে অবরূদ্ধ করে"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"বিজ্ঞপ্তি"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"এই বিজ্ঞপ্তিগুলি আপনাকে আর দেখানো হবে না"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"এই বিজ্ঞপ্তিগুলিকে আপনি সাধারণত বাতিল করেন। \nসেগুলি দেখতে চান?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"এই বিজ্ঞপ্তিগুলি পরেও দেখে যেতে চান?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"বিজ্ঞপ্তি বন্ধ করুন"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"দেখতে থাকুন"</string>
diff --git a/packages/SystemUI/res/values-bs/config.xml b/packages/SystemUI/res/values-bs/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-bs/config.xml
+++ b/packages/SystemUI/res/values-bs/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 7f8d001..676ecd0 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -104,12 +104,9 @@
<string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi raspored zadataka"</string>
<string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona za otisak prsta"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikacije"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Prostor za poruku za pomoć"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Dugme za uvećavanje u slučaju nekompatibilnosti."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Uvećani prikaz manjeg ekrana na većem ekranu."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth je povezan."</string>
@@ -210,13 +207,13 @@
<string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Uključen način rada u avionu."</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Način rada u avionu je isključen."</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Način rada u avionu je uključen."</string>
- <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"Opcija Ne ometaj je uključena, čut će se samo prioritetna obavještenja."</string>
- <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"Opcija Ne ometaj je uključena, potpuna tišina."</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"Opcija Ne ometaj je uključena, čut će se samo alarmi."</string>
+ <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"Način rada Ne ometaj je uključen, čut će se samo prioritetna obavještenja."</string>
+ <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"Način rada Ne ometaj je uključen, potpuna tišina."</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"Način rada Ne ometaj je uključen, čut će se samo alarmi."</string>
<string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"Ne ometaj."</string>
- <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"Opcija Ne ometaj je isključena."</string>
- <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"Opcija Ne ometaj je isključena."</string>
- <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"Opcija Ne ometaj je uključena."</string>
+ <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"Način rada Ne ometaj je isključen."</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"Način rada Ne ometaj je isključen."</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"Način rada Ne ometaj je uključen."</string>
<string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"Bluetooth."</string>
<string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth isključen."</string>
<string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth uključen."</string>
@@ -313,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi isključen"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi uključen"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nema dostupnih Wi-Fi mreža"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Emitiranje"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Prebacivanje"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovani uređaj"</string>
@@ -329,9 +328,12 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Povezivanje..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Dijeljenje veze"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Pristupna tačka"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Uključivanje…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d uređaj</item>
+ <item quantity="few">%d uređaja</item>
+ <item quantity="other">%d uređaja</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavještenja"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Prijenos podataka na mobilnoj mreži"</string>
@@ -341,10 +343,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Iskorišteno <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ograničenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Radni profil"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Isključena su obavještenja i aplikacije"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svjetlo"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Uključuje se u suton"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Do svitanja"</string>
@@ -362,8 +362,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> je onemogućena u sigurnom načinu rada."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Obriši sve"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Povucite ovdje za korištenje podijeljenog ekrana"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Prevucite prema gore za promjenu aplikacije"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podjela po vertikali"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođena podjela"</string>
@@ -504,11 +503,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Isključi sada"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Proširi"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skupi"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekran je prikačen"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Ekran ostaje prikazan ovako dok ga ne otkačite. Da ga otkačite, dodirnite i držite dugme Nazad."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ekran ostaje prikazan ovako dok ga ne otkačite. Da ga otkačite, dodirnite i držite dugme Pregled."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Razumijem"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Pojavit će se sljedeći put kada opciju uključite u postavkama."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
@@ -590,8 +603,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Uz kontrolu obavještenja o napajanju, možete postaviti nivo značaja obavještenja iz aplikacije, i to od nivoa 0 do 5. \n\n"<b>"Nivo 5"</b>" \n- Prikaži na vrhu liste obavještenja \n- Dopusti prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nvio 4"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nivo 3"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n\n"<b>"Nivo 2"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n\n"<b>"Nivo 1"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikada ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n- Sakrij sa ekrana za zaključavanje i statusne trake \n- Prikaži na dnu liste obavještenja \n\n"<b>"Nivo 0"</b>" \n- Blokiraj sva obavještenja iz aplikacije"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Obavještenja"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nećete više vidjeti ova obavještenja"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Obično odbacujete ova obavještenja. \nNastaviti ih prikazivati?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Nastaviti prikazivanje ovih obavještenja?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Zaustavi obavještenja"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivanje"</string>
@@ -668,7 +680,7 @@
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikazati sa kontrolama jačine zvuka"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne ometaj"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Prečica za dugmad za Jačinu zvuka"</string>
- <string name="volume_up_silent" msgid="7141255269783588286">"Deaktiviraj režim Ne ometaj kada se zvuk pojača"</string>
+ <string name="volume_up_silent" msgid="7141255269783588286">"Deaktiviraj način rada Ne ometaj kada se zvuk pojača"</string>
<string name="battery" msgid="7498329822413202973">"Baterija"</string>
<string name="clock" msgid="7416090374234785905">"Sat"</string>
<string name="headset" msgid="4534219457597457353">"Slušalice s mikrofonom"</string>
@@ -802,10 +814,10 @@
<string name="mobile_data" msgid="7094582042819250762">"Prijenos podataka na mobilnoj mreži"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi veza je isključena"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je isključen"</string>
- <string name="dnd_is_off" msgid="6167780215212497572">"Opcija Ne ometaj je isključena"</string>
+ <string name="dnd_is_off" msgid="6167780215212497572">"Način rada Ne ometaj je isključen"</string>
<string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"Opciju Ne ometaju uključilo je automatsko pravilo (<xliff:g id="ID_1">%s</xliff:g>)."</string>
- <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"Opciju Ne ometaj uključila je aplikacija <xliff:g id="ID_1">%s</xliff:g>."</string>
- <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"Opciju Ne ometaj uključilo je automatsko pravilo ili aplikacija."</string>
+ <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"Način rada Ne ometaj uključila je aplikacija <xliff:g id="ID_1">%s</xliff:g>."</string>
+ <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"Način rada Ne ometaj uključilo je automatsko pravilo ili aplikacija."</string>
<string name="qs_dnd_until" msgid="3469471136280079874">"Do <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="qs_dnd_keep" msgid="1825009164681928736">"Zadrži"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"Zamijeni"</string>
diff --git a/packages/SystemUI/res/values-ca/config.xml b/packages/SystemUI/res/values-ca/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ca/config.xml
+++ b/packages/SystemUI/res/values-ca/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8d61a9a..4eff30d 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -33,13 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continu"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificacions"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Queda poca bateria"</string>
- <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Queda poca bateria. Activa el mode Estalvi de bateria."</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Queda poca bateria. Activa el mode d\'estalvi de bateria."</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>."</string>
<string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>; temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>; temps restant aproximat: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>. La funció Estalvi de bateria està activada."</string>
- <string name="invalid_charger" msgid="4549105996740522523">"Pujada d\'USB no admesa.\nUtilitza només el carregador proporcionat."</string>
- <string name="invalid_charger_title" msgid="3515740382572798460">"La pujada per USB no és compatible."</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Càrrega per USB no admesa.\nUtilitza només el carregador proporcionat."</string>
+ <string name="invalid_charger_title" msgid="3515740382572798460">"La càrrega per USB no és compatible."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Fes servir només el carregador proporcionat amb el dispositiu."</string>
<string name="battery_low_why" msgid="4553600287639198111">"Configuració"</string>
<string name="battery_saver_confirmation_title" msgid="2052100465684817154">"Vols activar la funció Estalvi de bateria?"</string>
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"obre la càmera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el disseny de la tasca nova"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta digital"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icona d\'aplicació"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Àrea de missatge d\'ajuda"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connectat."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desconnectada"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"La Wi-Fi està activada"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No hi ha cap xarxa Wi-Fi disponible"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Emet"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"En emissió"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositiu sense nom"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"S\'està connectant..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Compartició de xarxa"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Punt d\'accés Wi-Fi"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"S\'està activant..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d dispositius</item>
+ <item quantity="one">%d dispositiu</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacions"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Llanterna"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dades mòbils"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Utilitzats: <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Límit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertiment: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Perfil professional"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Les notificacions i les aplicacions estan desactivades"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Llum nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"A la posta de sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Fins a l\'alba"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"En mode segur, l\'aplicació <xliff:g id="APP">%s</xliff:g> està desactivada."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Esborra-ho tot"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrossega-ho aquí per utilitzar la pantalla dividida"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Llisca cap amunt per canviar d\'aplicació"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisió horitzontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisió vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisió personalitzada"</string>
@@ -370,7 +368,7 @@
<string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Divideix la pantalla cap a la dreta"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"S\'està carregant"</string>
- <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> per completar la pujada"</string>
+ <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> per completar la càrrega"</string>
<string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"No s\'està carregant"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"És possible que la xarxa\nestigui controlada"</string>
<string name="description_target_search" msgid="3091587249776033139">"Cerca"</string>
@@ -397,9 +395,9 @@
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Silenci\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Només\ninterr. prior."</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Només\nalarmes"</string>
- <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la pujada)"</string>
- <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Pujada ràpida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Pujada lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la càrrega)"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Càrrega ràpida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Càrrega lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Canvia d\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Desactiva ara"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Amplia"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Replega"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"La pantalla està fixada"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, toca i mantén premudes els botons Enrere i Aplicacions recents."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, toca i mantén premut el botó Aplicacions recents."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"D\'acord"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gràcies"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vols amagar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tornarà a mostrar-se la propera vegada que l\'activis a la configuració."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Amaga"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Amb els controls de notificació millorats, pots establir un nivell d\'importància d\'entre 0 i 5 per a les notificacions d\'una aplicació. \n\n"<b>"Nivell 5"</b>" \n- Mostra les notificacions a la part superior de la llista \n- Permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 4"</b>" \n- No permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 3"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n\n"<b>"Nivell 2"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- Les notificacions no poden emetre sons ni vibracions \n\n"<b>"Nivell 1"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- No activa mai el so ni la vibració \n- Amaga les notificacions de la pantalla de bloqueig i de la barra d\'estat \n- Mostra les notificacions a la part inferior de la llista \n\n"<b>"Nivell 0"</b>" \n- Bloqueja totes les notificacions de l\'aplicació"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacions"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ja no veuràs aquestes notificacions"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Normalment ignores aquestes notificacions. \nVols que es continuïn mostrant?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vols continuar rebent aquestes notificacions?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Deixa d\'enviar notificacions"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continua rebent"</string>
@@ -614,7 +625,7 @@
<item quantity="one">%d minut</item>
</plurals>
<string name="battery_panel_title" msgid="7944156115535366613">"Consum de la bateria"</string>
- <string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la pujada"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la càrrega"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Estalvi de bateria"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Redueix el rendiment i les dades en segon pla"</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/config.xml b/packages/SystemUI/res/values-cs/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-cs/config.xml
+++ b/packages/SystemUI/res/values-cs/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3c3e1d4..faa1f1f 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -105,12 +105,9 @@
<string name="camera_label" msgid="7261107956054836961">"spustit fotoaparát"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Vybrat nové rozvržení úkolů"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otisku prstu"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ikona aplikace"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Oblast pro zprávu nápovědy"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Rozhraní Bluetooth je připojeno."</string>
@@ -317,6 +314,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi vypnuta"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi je zapnutá"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Žádné sítě Wi-Fi nejsou k dispozici"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Odeslat"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Odesílání"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nepojmenované zařízení"</string>
@@ -333,9 +332,13 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Připojování..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Sdílené připojení"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Zapínání..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="few">%d zařízení</item>
+ <item quantity="many">%d zařízení</item>
+ <item quantity="other">%d zařízení</item>
+ <item quantity="one">%d zařízení</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Oznámení"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svítilna"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilní data"</string>
@@ -345,10 +348,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Využito: <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozornění při <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Pracovní profil"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Oznámení a aplikace jsou vypnuty"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noční režim"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Zapnout při soumraku"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Do svítání"</string>
@@ -366,8 +367,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikace <xliff:g id="APP">%s</xliff:g> je v nouzovém režimu zakázána."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vymazat vše"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Rozdělenou obrazovku můžete použít přetažením zde"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Přejetím nahoru přepnete aplikace"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vodorovné rozdělení"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikální rozdělení"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Vlastní rozdělení"</string>
@@ -508,11 +508,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Vypnout"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozbalit"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sbalit"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Přepnout zařízení pro výstup"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Obrazovka je připnuta"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítek Zpět a Přehled."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítka Přehled."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Rozumím"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, děkuji"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skrýt <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tato položka se znovu zobrazí, až ji v nastavení znovu zapnete."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skrýt"</string>
@@ -592,8 +605,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Rozšířené ovládací prvky oznámení umožňují nastavit úroveň důležitosti oznámení aplikace od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazit na začátku seznamu oznámení \n– Povolit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 4"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 3"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n\n"<b>"Úroveň 2"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat žádný zvukový signál ani nevibrovat \n\n"<b>"Úroveň 1"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat zvukový signál ani nevibrovat \n– Skrýt z obrazovky uzamčení a stavového řádku \n– Zobrazovat na konci seznamu oznámení \n\n"<b>";Úroveň 0"</b>" \n– Blokovat všechna oznámení z aplikace"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Oznámení"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Tato oznámení již nebudete dostávat"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Tato oznámení obvykle odmítáte. \nChcete je nadále zobrazovat?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Mají se tato oznámení nadále zobrazovat?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Přestat zobrazovat oznámení"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Nadále zobrazovat"</string>
diff --git a/packages/SystemUI/res/values-da/config.xml b/packages/SystemUI/res/values-da/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-da/config.xml
+++ b/packages/SystemUI/res/values-da/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-de/config.xml b/packages/SystemUI/res/values-de/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-de/config.xml
+++ b/packages/SystemUI/res/values-de/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-el/config.xml b/packages/SystemUI/res/values-el/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-el/config.xml
+++ b/packages/SystemUI/res/values-el/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/config.xml b/packages/SystemUI/res/values-en-rAU/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-en-rAU/config.xml
+++ b/packages/SystemUI/res/values-en-rAU/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 3f6d5e5..3e9e3a2 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi On"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No Wi-Fi networks available"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Turning on..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d devices</item>
+ <item quantity="one">%d device</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torch"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Turn off now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Switch output device"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"This keeps it in view until you unpin. Touch & hold Back and Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch & hold Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/config.xml b/packages/SystemUI/res/values-en-rCA/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-en-rCA/config.xml
+++ b/packages/SystemUI/res/values-en-rCA/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index d49c25a..e5f4ece 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi On"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No Wi-Fi networks available"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Turning on..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d devices</item>
+ <item quantity="one">%d device</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Flashlight"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Turn off now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Switch output device"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"This keeps it in view until you unpin. Touch & hold Back and Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch & hold Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/config.xml b/packages/SystemUI/res/values-en-rGB/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-en-rGB/config.xml
+++ b/packages/SystemUI/res/values-en-rGB/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 3f6d5e5..3e9e3a2 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi On"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No Wi-Fi networks available"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Turning on..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d devices</item>
+ <item quantity="one">%d device</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torch"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Turn off now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Switch output device"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"This keeps it in view until you unpin. Touch & hold Back and Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch & hold Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/config.xml b/packages/SystemUI/res/values-en-rIN/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-en-rIN/config.xml
+++ b/packages/SystemUI/res/values-en-rIN/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 3f6d5e5..3e9e3a2 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi On"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No Wi-Fi networks available"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Turning on..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d devices</item>
+ <item quantity="one">%d device</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torch"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Turn off now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Switch output device"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"This keeps it in view until you unpin. Touch & hold Back and Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch & hold Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/config.xml b/packages/SystemUI/res/values-en-rXC/config.xml
index f015d9b..5309563 100644
--- a/packages/SystemUI/res/values-en-rXC/config.xml
+++ b/packages/SystemUI/res/values-en-rXC/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 0571c1f..35cef20 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"open camera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Application icon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Help message area"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi On"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No Wi-Fi networks available"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connecting..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Turning on..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d devices</item>
+ <item quantity="one">%d device</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Flashlight"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Turn off now"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expand"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Collapse"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Switch output device"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Screen is pinned"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"This keeps it in view until you unpin. Touch & hold Back and Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"This keeps it in view until you unpin. Touch & hold Overview to unpin."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No thanks"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"You usually dismiss these notifications. \nKeep showing them?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/config.xml b/packages/SystemUI/res/values-es-rUS/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-es-rUS/config.xml
+++ b/packages/SystemUI/res/values-es-rUS/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-es/config.xml b/packages/SystemUI/res/values-es/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-es/config.xml
+++ b/packages/SystemUI/res/values-es/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5cc6ab6..2a409a5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -88,7 +88,7 @@
<string name="accessibility_home" msgid="8217216074895377641">"Inicio"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menú"</string>
<string name="accessibility_accessibility_button" msgid="7601252764577607915">"Accesibilidad"</string>
- <string name="accessibility_rotate_button" msgid="7402949513740253006">"Girar la pantalla"</string>
+ <string name="accessibility_rotate_button" msgid="7402949513740253006">"Girar pantalla"</string>
<string name="accessibility_recent" msgid="5208608566793607626">"Aplicaciones recientes"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string>
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar diseño de tarea nueva"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icono de huella digital"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icono de aplicación"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área de mensaje de ayuda"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -313,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivado"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi activada"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"No hay ninguna red Wi-Fi disponible"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Enviar"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Enviando"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sin nombre"</string>
@@ -329,9 +328,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Conectando..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Compartir conexión"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Activando..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d dispositivos</item>
+ <item quantity="one">%d dispositivo</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Linterna"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Datos móviles"</string>
@@ -341,10 +342,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> usado"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Perfil de trabajo"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Las notificaciones y las aplicaciones están desactivadas"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Al atardecer"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Hasta el amanecer"</string>
@@ -362,8 +361,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrastra hasta aquí para utilizar la pantalla dividida"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Desliza el dedo hacia arriba para cambiar de aplicación"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
@@ -504,11 +502,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Desactivar"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Mostrar"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Ocultar"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Cambiar dispositivo de salida"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Pantalla fijada"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsados los botones Atrás y Aplicaciones recientes."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsado el botón Aplicaciones recientes."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendido"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gracias"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá a aparecer la próxima vez que actives esta opción en Ajustes."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
@@ -588,8 +599,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Los controles de energía de las notificaciones permiten establecer un nivel de importancia de 0 a 5 para las notificaciones de las aplicaciones. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones \n- Permitir interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 4"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 3"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n\n"<b>"Nivel 2"</b>" \n- Evitar interrumpir en el modo de pantalla completa\n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n\n"<b>"Nivel 1"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n- Ocultar de la pantalla de bloqueo y de la barra de estado \n- Mostrar en la parte inferior de la lista de notificaciones \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la aplicación"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificaciones"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"No volverás a ver estas notificaciones"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Normalmente ignoras estas notificaciones. \n¿Quieres seguir viéndolas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"¿Quieres seguir viendo estas notificaciones?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Detener las notificaciones"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Seguir mostrando"</string>
diff --git a/packages/SystemUI/res/values-et/config.xml b/packages/SystemUI/res/values-et/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-et/config.xml
+++ b/packages/SystemUI/res/values-et/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/config.xml b/packages/SystemUI/res/values-eu/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-eu/config.xml
+++ b/packages/SystemUI/res/values-eu/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/config.xml b/packages/SystemUI/res/values-fa/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-fa/config.xml
+++ b/packages/SystemUI/res/values-fa/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/config.xml b/packages/SystemUI/res/values-fi/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-fi/config.xml
+++ b/packages/SystemUI/res/values-fi/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 34072db..e7fcb43 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"avaa kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Valitse uusi tehtävien asettelu"</string>
<string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sormenjälkikuvake"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Sovelluskuvake"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Ohjeviestialue"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth yhdistetty."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi-yhteys pois käytöstä"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi on käytössä"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Ei Wi-Fi-verkkoja käytettävissä"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Suoratoisto"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Lähetetään"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nimetön laite"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Yhdistetään…"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jaettu yhteys"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Otetaan käyttöön…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d laitetta</item>
+ <item quantity="one">%d laite</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ilmoitukset"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Taskulamppu"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiilidata"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"käytetty <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"kiintiö <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> – varoitus"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Työprofiili"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Ilmoitukset ja sovellukset on poistettu käytöstä"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Yövalo"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Auringon laskiessa"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Auringonnousuun"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> on poistettu käytöstä vikasietotilassa."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tyhjennä kaikki"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Jaa näyttö vetämällä tähän."</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Vaihda sovellusta pyyhkäisemällä ylös"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vaakasuuntainen jako"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pystysuuntainen jako"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Muokattu jako"</string>
@@ -502,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Sammuta nyt"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Laajenna."</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Tiivistä."</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Vaihda toistolaitetta"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Näyttö on kiinnitetty"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Pysyy näkyvissä, kunnes irrotat sen. Irrota painamalla pitkään Edellinen ja Viimeisimmät."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Pysyy näkyvissä, kunnes irrotat sen. Irrota painamalla pitkään Viimeisimmät."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Selvä"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ei kiitos"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Piilotetaanko <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Se tulee takaisin näkyviin, kun seuraavan kerran otat sen käyttöön asetuksissa."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Piilota"</string>
@@ -586,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Ilmoitusten tehohallinnan avulla voit määrittää sovelluksen ilmoituksille tärkeystason väliltä 0–5. \n\n"<b>"Taso 5"</b>" \n– Ilmoitukset näytetään ilmoitusluettelon yläosassa \n– Näkyminen koko näytön tilassa sallitaan \n– Ilmoitukset kurkistavat aina näytölle\n\n"<b>"Taso 4"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ilmoitukset kurkistavat aina näytölle \n\n"<b>"Taso 3"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n\n"<b>"Taso 2"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n\n"<b>"Taso 1"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n– Ilmoitukset piilotetaan lukitusnäytöltä ja tilapalkista \n– Ilmoitukset näytetään ilmoitusluettelon alaosassa \n\n"<b>"Taso 0"</b>" \n– Kaikki sovelluksen ilmoitukset estetään"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Ilmoitukset"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Et näe näitä ilmoituksia enää"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Hylkäät yleensä nämä ilmoitukset. \nHaluatko, että niitä näytetään myös jatkossa?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Jatketaanko näiden ilmoitusten näyttämistä?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Lopeta ilmoitukset"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Jatka näyttämistä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/config.xml b/packages/SystemUI/res/values-fr-rCA/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-fr-rCA/config.xml
+++ b/packages/SystemUI/res/values-fr-rCA/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 16a4eac..56ca97c 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -88,6 +88,7 @@
<string name="accessibility_home" msgid="8217216074895377641">"Domicile"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menu"</string>
<string name="accessibility_accessibility_button" msgid="7601252764577607915">"Accessibilité"</string>
+ <string name="accessibility_rotate_button" msgid="7402949513740253006">"Faire pivoter l\'écran"</string>
<string name="accessibility_recent" msgid="5208608566793607626">"Aperçu"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
@@ -102,6 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"Ouvrir l\'appareil photo"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau format de tâche"</string>
<string name="cancel" msgid="6442560571259935130">"Annuler"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icône de l\'application"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Zone de message d\'aide"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connecté"</string>
@@ -180,7 +184,8 @@
<string name="accessibility_ringer_silent" msgid="9061243307939135383">"Sonnerie en mode silencieux"</string>
<!-- no translation found for accessibility_casting (6887382141726543668) -->
<skip />
- <string name="accessibility_work_mode" msgid="2478631941714607225">"Mode Travail"</string>
+ <!-- no translation found for accessibility_work_mode (702887484664647430) -->
+ <skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
@@ -277,6 +282,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> appareils)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"BLUETOOTH DÉSACTIVÉ"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Aucun des appareils associés n\'est disponible"</string>
+ <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="7106697106764717416">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
+ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
+ <string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Écouteurs"</string>
+ <string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Entrée"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosité"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotation automatique"</string>
<string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"Rotation automatique de l\'écran"</string>
@@ -301,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi activé"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Aucun réseau Wi-Fi à proximité"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Diffuser"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Diffusion"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Appareil sans nom"</string>
@@ -317,6 +328,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connexion en cours…"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès sans fil"</string>
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Activation en cours…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d appareil</item>
+ <item quantity="other">%d appareils</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampe de poche"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Données cellulaires"</string>
@@ -326,8 +342,13 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Quantité de données utilisées :<xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode Travail"</string>
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Profil professionnel"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Les notifications et les applications sont désactivées"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éclairage nocturne"</string>
+ <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Au coucher du soleil"</string>
+ <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Jusqu\'au lev. soleil"</string>
+ <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_night_secondary_label_until" msgid="8664820079774824618">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC activée"</string>
@@ -340,6 +361,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sans échec."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Effacer tout"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Glissez l\'élément ici pour utiliser l\'écran partagé"</string>
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Balayez vers le haut pour changer d\'application"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
@@ -480,11 +502,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Désactiver maintenant"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Développer"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Réduire"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Changer d\'appareil de sortie"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"L\'écran est épinglé"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Cet écran est épinglé jusqu\'à ce que vous annuliez l\'opération. Pour annuler l\'épinglage, maintenez le doigt sur « Retour » et « Aperçu »."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Cet écran est épinglé jusqu\'à ce que vous annuliez l\'opération. Pour annuler l\'épinglage, maintenez le doigt sur « Aperçu »."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Non, merci"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Cet élément réapparaîtra la prochaine fois que vous l\'activerez dans les paramètres."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Masquer"</string>
@@ -564,9 +599,10 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Avec les réglages avancés des notifications, vous pouvez définir un degré d\'importance de 0 à 5 pour les notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher dans le haut de la liste des notifications \n- Autoriser les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 4"</b>" \n- Empêcher les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 3"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n\n"<b>"Niveau 2"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n\n"<b>"Niveau 1"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n- Masquer de l\'écran de verrouillage et de la barre d\'état status bar \n- Afficher dans le bas de la liste des notifications \n\n"<b>"Level 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne verrez plus ces notifications"</string>
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Vous ignorez habituellement ces notifications. \nSouhaitez-vous continuer à les afficher?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuer à afficher ces notifications?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string>
- <string name="inline_keep_button" msgid="6665940297019018232">"Continuez à afficher"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuer à afficher"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer à afficher les notifications de cette application?"</string>
<string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont ouverts"</string>
@@ -658,7 +694,8 @@
<item msgid="1545641631806817203">"Presse-papiers"</item>
<item msgid="5742013440802239414">"Code de touche"</item>
<item msgid="8802889973626281575">"Sélecteur de clavier"</item>
- <item msgid="8175437057325747277">"Aucun"</item>
+ <item msgid="7095517796293767867">"Suggestion de rotation"</item>
+ <item msgid="8494159969042135235">"Aucun"</item>
</string-array>
<string-array name="nav_bar_layouts">
<item msgid="8077901629964902399">"Normale"</item>
@@ -782,4 +819,10 @@
<string name="running_foreground_services_msg" msgid="6326247670075574355">"Touchez pour afficher des détails sur l\'utilisation de la pile et des données"</string>
<string name="data_usage_disable_mobile" msgid="5116269981510015864">"Désactiver les données cellulaires?"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"Une application obscurcit une demande d\'autorisation, alors Paramètres ne peut pas vérifier votre réponse."</string>
+ <string name="slice_permission_title" msgid="7465009437851044444">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches?"</string>
+ <string name="slice_permission_text_1" msgid="3514586565609596523">"- Il peut lire de l\'information de <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="slice_permission_text_2" msgid="3146758297471143723">"- Il peut effectuer des actions dans <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="slice_permission_checkbox" msgid="7986504458640562900">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à afficher des tranches de n\'importe quelle application"</string>
+ <string name="slice_permission_allow" msgid="2340244901366722709">"Autoriser"</string>
+ <string name="slice_permission_deny" msgid="7683681514008048807">"Refuser"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/config.xml b/packages/SystemUI/res/values-fr/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-fr/config.xml
+++ b/packages/SystemUI/res/values-fr/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/config.xml b/packages/SystemUI/res/values-gl/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-gl/config.xml
+++ b/packages/SystemUI/res/values-gl/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/config.xml b/packages/SystemUI/res/values-gu/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-gu/config.xml
+++ b/packages/SystemUI/res/values-gu/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 27d3ba8..7c530cd 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"કૅમેરો ખોલો"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"નવું કાર્ય લેઆઉટ પસંદ કરો"</string>
<string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ફિંગરપ્રિન્ટનું આઇકન"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ઍપ્લિકેશનનું આઇકન"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"સહાય સંદેશનું ક્ષેત્ર"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"સુસંગતતા ઝૂમ બટન."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"નાનીથી મોટી સ્ક્રીન પર ઝૂમ કરો."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"બ્લૂટૂથ કનેક્ટ થયું."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"વાઇ-ફાઇ બંધ"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"વાઇ-ફાઇ ચાલુ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"કોઈ વાઇ-ફાઇ નેટવર્ક્સ ઉપલબ્ધ નથી"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"કાસ્ટ કરો"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"કાસ્ટ કરી રહ્યાં છે"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"અનામાંકિત ઉપકરણ"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"કનેક્ટ કરી રહ્યું છે..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ટિથરિંગ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"હૉટસ્પૉટ"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ચાલુ કરી રહ્યાં છીએ…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d ઉપકરણ</item>
+ <item quantity="other">%d ઉપકરણો</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"નોટિફિકેશનો"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ફ્લેશલાઇટ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"મોબાઇલ ડેટા"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> વાપર્યો"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> મર્યાદા"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ચેતવણી"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"કાર્યાલયની પ્રોફાઇલ"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"નોટિફિકેશન અને ઍપ બંધ છે"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"રાત્રિ પ્રકાશ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"સૂર્યાસ્ત વખતે"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"સૂર્યોદય સુધી"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"સુરક્ષિત મોડમાં <xliff:g id="APP">%s</xliff:g> અક્ષમ કરેલ છે."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"બધું સાફ કરો"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરવા માટે અહીં ખેંચો"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ઍપ સ્વિચ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"આડું વિભક્ત કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ઊભું વિભક્ત કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"કસ્ટમ વિભક્ત કરો"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"હમણાં બંધ કરો"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"વિસ્તૃત કરો"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"સંકુચિત કરો"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"સ્ક્રીન પિન કરેલ છે"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે પાછળ અને ઝલકને સ્પર્શ કરી રાખો."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે ઝલકને સ્પર્શ કરી રાખો."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"સમજાઈ ગયું"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ના, આભાર"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ને છુપાવીએ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"તે સેટિંગ્સમાં તમે તેને ચાલુ કરશો ત્યારે આગલી વખતે ફરીથી દેખાશે."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"છુપાવો"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"પાવર સૂચના નિયંત્રણો સાથે, તમે ઍપની સૂચનાઓ માટે 0 થી 5 સુધીના મહત્વના સ્તરને સેટ કરી શકો છો. \n\n"<b>"સ્તર 5"</b>" \n- સૂચના સૂચિની ટોચ પર બતાવો \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 4"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 3"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n\n"<b>"સ્તર 2"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n\n"<b>"સ્તર 1"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n- લૉક સ્ક્રીન અને સ્ટેટસ બારથી છુપાવો \n- સૂચના સૂચિના તળિયા પર બતાવો \n\n"<b>"સ્તર 0"</b>" \n- ઍપની તમામ સૂચનાઓને બ્લૉક કરો"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"નોટિફિકેશનો"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"તમને હવેથી આ નોટિફિકેશન દેખાશે નહીં"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"તમે સામાન્ય રીતે આ નોટીફિકેશનને છોડી દો છો. \nતેમને બતાવવાનું ચાલુ રાખીએ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"આ નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"નોટિફિકેશન બંધ કરો"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"બતાવવાનું ચાલુ રાખો"</string>
diff --git a/packages/SystemUI/res/values-hi/config.xml b/packages/SystemUI/res/values-hi/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-hi/config.xml
+++ b/packages/SystemUI/res/values-hi/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index bb2e897..379d1ab 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"कैमरा खोलें"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नया कार्य लेआउट चुनें"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फ़िंगरप्रिंट आइकॉन"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ऐप्लिकेशन आइकॉन"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"सहायता का मैसेज दिखाने की जगह"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"संगतता ज़ूम बटन."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"छोटी से बड़ी स्क्रीन पर ज़ूम करें."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लूटूथ कनेक्ट किया गया."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"वाई-फ़ाई बंद"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"वाई-फ़ाई चालू है"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"कोई भी वाई-फ़ाई नेटवर्क उपलब्ध नहीं है"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"कास्ट करें"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"कास्टिंग"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"अनाम डिवाइस"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"कनेक्ट हो रहा है..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"टेदरिंग"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"हॉटस्पॉट"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"चालू किया जा रहा है..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d डिवाइस</item>
+ <item quantity="other">%d डिवाइस</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचनाएं"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"फ़्लैशलाइट"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"मोबाइल डेटा"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> उपयोग किया गया"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"कार्य प्रोफ़ाइल"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"सूचनाएं और ऐप्लिकेशन बंद हैं"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"नाइट लाइट"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"शाम को चालू की जाएगी"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"सुबह तक चालू रहेगी"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> को सुरक्षित-मोड में बंद किया गया."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"स्क्रीन के दो हिस्से में बंट जाने, स्पिल्ट स्क्रीन, का इस्तेमाल करने के लिए यहां खींचें और छोडें"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ऐप्लिकेशन बदलने के लिए ऊपर स्वाइप करें"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज रूप से विभाजित करें"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"लम्बवत रूप से विभाजित करें"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"अपने मुताबिक बांटें"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"अभी बंद करें"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तार करें"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"छोटा करें"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"स्क्रीन पिन कर दी गई है"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"इससे वह तब तक दिखता रहता है जब तक कि आप उसे अनपिन नहीं कर देते. अनपिन करने के लिए, \'वापस जाएं\' और \'खास जानकारी\' को दबाकर रखें."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"इससे वह तब तक दिखता रहता है जब तक कि आप उसे अनपिन नहीं कर देते. अनपिन करने के लिए, \'खास जानकारी\' को दबाकर रखें."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ठीक है"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"नहीं, रहने दें"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> को छिपाएं?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"जब आप उसे अगली बार सेटिंग में चालू करेंगे तो वह फिर से दिखाई देगी."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"छिपाएं"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"पावर सूचना नियंत्रण के ज़रिये, आप किसी ऐप की सूचना को उसकी अहमियत के हिसाब से 0 से 5 के लेवल पर सेट कर सकते हैं.\n\n"<b>"लेवल 5"</b>" \n- सूचना सूची में सबसे ऊपर दिखाएं \n- पूरे स्क्रीन को ढंकने की अनुमति दें \n- लगातार देखते रहें \n\n"<b>" लेवल 4"</b>" \n- पूरे स्क्रीन को ढंकें \n- लगातार देखते रहें \n\n"<b>"लेवल 3"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n-कभी भी न देखें \n\n"<b>"लेवल 2"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n\n"<b>"लेवल 1"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी न देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n- लॉक स्क्रीन और स्टेटस बार से छिपाएं \n- सूचना सूची के नीचे दिखाएं \n\n"<b>"लेवल 0"</b>" \n- ऐप्लिकेशन की सभी सूचनाएं रोक दें"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"अब आपको ये सूचनाएं दिखाई नहीं देंगी"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"अाप अक्सर इन सूचनाओं को खारिज कर देते हैं. \nआगे भी इन्हें देखना जारी रखना चाहते हैं?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ये सूचनाएं दिखाना जारी रखें?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"सूचनाएं दिखाना बंद करें"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"दिखाना जारी रखें"</string>
@@ -807,10 +818,10 @@
<string name="running_foreground_services_msg" msgid="6326247670075574355">"बैटरी और डेटा खर्च की जानकारी के लिए छूएं"</string>
<string name="data_usage_disable_mobile" msgid="5116269981510015864">"मोबाइल डेटा बंद करना चाहते हैं?"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"ऐप की वजह से मंज़ूरी के अनुरोध को समझने में दिक्कत हो रही है, इसलिए सेटिंग से आपके जवाब की पुष्टि नहीं हो पा रही है."</string>
- <string name="slice_permission_title" msgid="7465009437851044444">"<xliff:g id="APP_0">%1$s</xliff:g> को <xliff:g id="APP_2">%2$s</xliff:g> से हिस्से (स्लाइस) दिखाने की मंज़ूरी दें?"</string>
+ <string name="slice_permission_title" msgid="7465009437851044444">"<xliff:g id="APP_0">%1$s</xliff:g> को <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें?"</string>
<string name="slice_permission_text_1" msgid="3514586565609596523">"- यह <xliff:g id="APP">%1$s</xliff:g> से सूचना पढ़ सकता है"</string>
<string name="slice_permission_text_2" msgid="3146758297471143723">"- यह <xliff:g id="APP">%1$s</xliff:g> में कार्रवाई कर सकता है"</string>
- <string name="slice_permission_checkbox" msgid="7986504458640562900">"<xliff:g id="APP">%1$s</xliff:g> को किसी भी ऐप्लिकेशन से हिस्से (स्लाइस) दिखाने की मंज़ूरी दें."</string>
+ <string name="slice_permission_checkbox" msgid="7986504458640562900">"<xliff:g id="APP">%1$s</xliff:g> को किसी भी ऐप्लिकेशन के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें"</string>
<string name="slice_permission_allow" msgid="2340244901366722709">"मंज़ूरी दें"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"नामंज़ूर करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/config.xml b/packages/SystemUI/res/values-hr/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-hr/config.xml
+++ b/packages/SystemUI/res/values-hr/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/config.xml b/packages/SystemUI/res/values-hu/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-hu/config.xml
+++ b/packages/SystemUI/res/values-hu/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/config.xml b/packages/SystemUI/res/values-hy/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-hy/config.xml
+++ b/packages/SystemUI/res/values-hy/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-in/config.xml b/packages/SystemUI/res/values-in/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-in/config.xml
+++ b/packages/SystemUI/res/values-in/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-is/config.xml b/packages/SystemUI/res/values-is/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-is/config.xml
+++ b/packages/SystemUI/res/values-is/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-it/config.xml b/packages/SystemUI/res/values-it/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-it/config.xml
+++ b/packages/SystemUI/res/values-it/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 880da63..89e9cc1 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"apri fotocamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Seleziona un nuovo layout per le attività"</string>
<string name="cancel" msgid="6442560571259935130">"Annulla"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona dell\'impronta digitale"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Icona dell\'applicazione"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Area dei messaggi di assistenza"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Pulsante zoom compatibilità."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom inferiore per schermo più grande."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth collegato."</string>
@@ -313,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi disattivato"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi attivo"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nessuna rete Wi-Fi disponibile"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Trasmetti"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"In trasmissione"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo senza nome"</string>
@@ -329,9 +328,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Connessione..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Attivazione…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d dispositivi</item>
+ <item quantity="one">%d dispositivo</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifiche"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torcia"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dati mobili"</string>
@@ -501,11 +502,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Disattiva ora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Espandi"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Comprimi"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Cambia dispositivo di uscita"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"La schermata è fissata"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"La schermata rimane visibile finché non viene sganciata. Per sganciarla, tieni premuto Indietro e Panoramica."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"La schermata rimane visibile finché non viene sganciata. Per sganciarla, tieni premuto Panoramica."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, grazie"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Nascondere <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Verranno visualizzate di nuovo quando le riattiverai nelle impostazioni."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Nascondi"</string>
@@ -585,8 +599,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"I controlli di gestione delle notifiche ti consentono di impostare un livello di importanza compreso tra 0 e 5 per le notifiche di un\'app. \n\n"<b>"Livello 5"</b>" \n- Mostra in cima all\'elenco di notifiche \n- Consenti l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 4"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 3"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n\n"<b>"Livello 2"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n\n"<b>"Livello 1"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n- Nascondi da schermata di blocco e barra di stato \n- Mostra in fondo all\'elenco di notifiche \n\n"<b>"Livello 0"</b>" \n- Blocca tutte le notifiche dell\'app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifiche"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Non vedrai più queste notifiche"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"In genere ignori queste notifiche. \nVuoi continuare a riceverle?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuare a ricevere queste notifiche?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Interrompi la ricezione di notifiche"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continua a mostrare"</string>
diff --git a/packages/SystemUI/res/values-iw/config.xml b/packages/SystemUI/res/values-iw/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-iw/config.xml
+++ b/packages/SystemUI/res/values-iw/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/config.xml b/packages/SystemUI/res/values-ja/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ja/config.xml
+++ b/packages/SystemUI/res/values-ja/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/config.xml b/packages/SystemUI/res/values-ka/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ka/config.xml
+++ b/packages/SystemUI/res/values-ka/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/config.xml b/packages/SystemUI/res/values-kk/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-kk/config.xml
+++ b/packages/SystemUI/res/values-kk/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-km/config.xml b/packages/SystemUI/res/values-km/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-km/config.xml
+++ b/packages/SystemUI/res/values-km/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/config.xml b/packages/SystemUI/res/values-kn/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-kn/config.xml
+++ b/packages/SystemUI/res/values-kn/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index a8b3007..4b638f5 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ಅಪ್ಲಿಕೇಶನ್ ಐಕಾನ್"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ಸಹಾಯ ಸಂದೇಶ ಪ್ರದೇಶ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ಹೊಂದಾಣಿಕೆಯ ಝೂಮ್ ಬಟನ್."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ಚಿಕ್ಕ ಪರದೆಯಿಂದ ದೊಡ್ಡ ಪರದೆಗೆ ಝೂಮ್ ಮಾಡು."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ಬ್ಲೂಟೂತ್ ಸಂಪರ್ಕಗೊಂಡಿದೆ."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ವೈ-ಫೈ ಆಫ್"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"ವೈ-ಫೈ ಆನ್ ಆಗಿದೆ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"ಯಾವುದೇ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"ಬಿತ್ತರಿಸುವಿಕೆ"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ಹೆಸರಿಸದಿರುವ ಸಾಧನ"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ಟೆಥರಿಂಗ್"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ಹಾಟ್ಸ್ಪಾಟ್"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d ಸಾಧನಗಳು</item>
+ <item quantity="other">%d ಸಾಧನಗಳು</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ಫ್ಲಾಶ್ಲೈಟ್"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ಬಳಸಲಾಗಿದೆ"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಮಿತಿ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"ನೈಟ್ ಲೈಟ್"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"ಸೂರ್ಯಾಸ್ತದಲ್ಲಿ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"ಸೂರ್ಯೋದಯದ ತನಕ"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"ವಿಭಜಿತ ಪರದೆಯನ್ನು ಬಳಸಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ಈಗ ಆಫ್ ಮಾಡಿ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ವಿಸ್ತರಿಸು"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ಸಂಕುಚಿಸು"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"ಪರದೆಯನ್ನು ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"ನೀವು ಅನ್ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ ಹಾಗೂ ಅನ್ಪಿನ್ ಮಾಡಲು ಅವಲೋಕಿಸಿ."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"ನೀವು ಅನ್ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಅನ್ಪಿನ್ ಮಾಡಲು ಅವಲೋಕನವನ್ನು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ತಿಳಿಯಿತು"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ಧನ್ಯವಾದಗಳು"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ಮರೆಮಾಡುವುದೇ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ನೀವು ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಅದನ್ನು ಆನ್ ಮಾಡಿದಾಗ ಅದು ಮರುಕಾಣಿಸಿಕೊಳ್ಳುತ್ತದೆ."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ಮರೆಮಾಡಿ"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"ಪವರ್ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳ ಮೂಲಕ, ನೀವು ಅಪ್ಲಿಕೇಶನ್ಗಳ ಅಧಿಸೂಚನೆಗಳನ್ನು 0 ರಿಂದ 5 ರವರೆಗಿನ ಹಂತಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಬಹುದು. \n\n"<b>"ಹಂತ 5"</b>" \n- ಮೇಲಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ಅನುಮತಿಸಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ \n\n"<b>"ಹಂತ 4"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ\n\n"<b>"ಹಂತ 3"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n\n"<b>"ಹಂತ 2"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n\n"<b>"ಹಂತ 1"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n- ಸ್ಥಿತಿ ಪಟ್ಟಿ ಮತ್ತು ಲಾಕ್ ಪರದೆಯಿಂದ ಮರೆಮಾಡಿ \n- ಕೆಳಗಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n\n"<b>"ಹಂತ 0"</b>" \n- ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ನೀವು ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಇನ್ನು ಮುಂದೆ ನೋಡುವುದಿಲ್ಲ"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"ನೀವು ಸಾಮಾನ್ಯವಾಗಿ ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ವಜಾಗೊಳಿಸಿದ್ದೀರಿ. \nಅವುಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿಲ್ಲಿಸಿ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ತೋರಿಸುತ್ತಲಿರಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/config.xml b/packages/SystemUI/res/values-ko/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ko/config.xml
+++ b/packages/SystemUI/res/values-ko/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/config.xml b/packages/SystemUI/res/values-ky/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ky/config.xml
+++ b/packages/SystemUI/res/values-ky/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/config.xml b/packages/SystemUI/res/values-lo/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-lo/config.xml
+++ b/packages/SystemUI/res/values-lo/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 338da6c..1b56074 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ເປີດກ້ອງ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ເລືອກແຜນຜັງໜ້າວຽກໃໝ່"</string>
<string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ໄອຄອນລາຍນິ້ວມື"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ໄອຄອນແອັບພລິເຄຊັນ"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ຊ່ວຍພື້ນທີ່ຂໍ້ຄວາມ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ປຸ່ມຊູມທີ່ໃຊ້ຮ່ວມກັນໄດ້."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ຊູມຈໍນ້ອຍໄປເປັນຈໍຂະຫນາດໃຫຍ່."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ເຊື່ອມຕໍ່ Bluetooth ແລ້ວ."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ປິດ"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ເປີດ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"ບໍ່ມີເຄືອຂ່າຍ Wi-Fi ຢູ່"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"ການສົ່ງສັນຍານ"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"ກຳລັງສົ່ງສັນຍານ"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ອຸປະກອນບໍ່ມີຊື່"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"ກຳລັງເຊື່ອມຕໍ່..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ການປ່ອນສັນຍານ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ຮັອດສະປອດ"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ກຳລັງເປີດ..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d ອຸປະກອນ</item>
+ <item quantity="one">%d ອຸປະກອນ</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ການແຈ້ງເຕືອນ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ໄຟສາຍ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ອິນເຕີເນັດມືຖື"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"ໃຊ້ໄປແລ້ວ <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"ຈຳກັດ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"ຄຳເຕືອນ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"ແສງກາງຄືນ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"ເປີດຕອນຕາເວັນຕົກ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ຖືກປິດໃຊ້ໃນໂໝດຄວາມມປອດໄພ."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"ລຶບລ້າງທັງໝົດ"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"ລາກມາບ່ອນນີ້ເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ປັດຂື້ນເພື່ອສະຫຼັບແອັບ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ການແຍກລວງຂວາງ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ການແຍກລວງຕັ້ງ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ການແຍກກຳນົດເອງ"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ປິດດຽວນີ້"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ຂະຫຍາຍ"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ຫຍໍ້ລົງ"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"ປັກໝຸດໜ້າຈໍແລ້ວ"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"ນີ້ຈະສະແດງມັນໃນໜ້າຈໍຈົນກວ່າທ່ານຈະເຊົາປັກມຸດ. ໃຫ້ແຕະປຸ່ມກັບຄືນ ແລະ ປຸ່ມພາບຮວມຄ້າງໄວ້ເພື່ອຍົກເລີກການປັກມຸດ."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"ນີ້ຈະສະແດງມັນໃນໜ້າຈໍຈົນກວ່າທ່ານຈະເຊົາປັກມຸດ. ໃຫ້ແຕະປຸ່ມພາບຮວມຄ້າງໄວ້ເພື່ອຍົກເລີກການປັກມຸດ."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ບໍ່, ຂອບໃຈ"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ເຊື່ອງ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ຫຼືບໍ່?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ມັນຈະສະແດງຄືນໃໝ່ເມື່ອທ່ານເປີດນຳໃຊ້ມັນໃນການຕັ້ງຄ່າຄັ້ງຕໍ່ໄປ."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ເຊື່ອງ"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"ດ້ວຍການຄວບຄຸມການແຈ້ງເຕືອນ, ທ່ານສາມາດຕັ້ງລະດັບຄວາມສຳຄັນຈາກ 0 ຮອດ 5 ໃຫ້ກັບການແຈ້ງເຕືອນແອັບໃດໜຶ່ງໄດ້. \n\n"<b>"ລະດັບ 5"</b>" \n- ສະແດງຢູ່ເທິງສຸດຂອງລາຍການແຈ້ງເຕືອນ \n- ອະນຸຍາດໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 4"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 3"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n\n"<b>"ລະດັບ 2"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n\n"<b>"ລະດັບ 1"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n- ເຊື່ອງຈາກໜ້າຈໍລັອກ ແລະ ແຖບສະຖານະ \n- ສະແດງຢູ່ລຸ່ມສຸດຂອງລາຍການແຈ້ງເຕືອນ \n\n"<b>"ລະດັບ 0"</b>" \n- ປິດກັ້ນການແຈ້ງເຕືອນທັງໝົດຈາກແອັບ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ການແຈ້ງເຕືອນ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ທ່ານຈະບໍ່ໄດ້ຮັບການແຈ້ງເຕືອນເຫຼົ່ານີ້ອີກຕໍ່ໄປ"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"ໂດຍປົກກະຕິທ່ານປິດການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄວ້. \nສືບຕໍ່ສະແດງພວກມັນບໍ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ສະແດງການແຈ້ງເຕືອນເຫຼົ່ານີ້ຕໍ່ໄປບໍ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ຢຸດການແຈ້ງເຕືອນ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ສະແດງຕໍ່ໄປ"</string>
diff --git a/packages/SystemUI/res/values-lt/config.xml b/packages/SystemUI/res/values-lt/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-lt/config.xml
+++ b/packages/SystemUI/res/values-lt/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/config.xml b/packages/SystemUI/res/values-lv/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-lv/config.xml
+++ b/packages/SystemUI/res/values-lv/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/config.xml b/packages/SystemUI/res/values-mk/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-mk/config.xml
+++ b/packages/SystemUI/res/values-mk/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 9808ab6..07ea7aa 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"отвори камера"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Изберете нов распоред на задача"</string>
<string name="cancel" msgid="6442560571259935130">"Откажи"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатоци"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Икона за апликацијата"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Поле за пораки за помош"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Копче за компатибилност на зум."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумот е помал на поголем екран."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth е поврзан."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi е исклучено"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Вклучено е Wi-Fi"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Нема достапни Wi-Fi мрежи"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Емитувај"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Емитување"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Неименуван уред"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Се поврзува..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Поврзување"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка на пристап"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Се вклучува…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d уред</item>
+ <item quantity="other">%d уреди</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Известувања"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Светилка"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобилен интернет"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Исклучи сега"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Прошири"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Собери"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Префрлете го излезниот уред"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Екранот е прикачен"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Ќе се гледа сѐ додека не го откачите. Допрете и држете „Назад“ и „Краток преглед“ за откачување."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ќе се гледа сѐ додека не го откачите. Допрете и држете „Краток преглед“ за откачување."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Сфатив"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Не, фала"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сокриј <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ќе се појави повторно следниот пат кога ќе го вклучите во поставки."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Сокриј"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Со контролите за известувањата за напојување, може да поставите ниво на важност од 0 до 5 за известувањата на која било апликација. \n\n"<b>"Ниво 5"</b>" \n- Прикажувај на врвот на списокот со известувања \n- Дозволи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 4"</b>" \n- Спречи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 3"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n\n"<b>"Ниво 2"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n\n"<b>"Ниво 1"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n- Сокриј од заклучен екран и статусна лента \n- Прикажувај на дното на списокот со известувања \n\n"<b>"Ниво 0"</b>" \n- Блокирај ги сите известувања од апликацијата"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Известувања"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Веќе нема да ги гледате овие известувања"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Обично ги отфрлате известувањава. \nДа продолжат да се прикажуваат?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Дали да продолжат да се прикажуваат известувањава?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Запри ги известувањата"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Продолжи да ги прикажуваш"</string>
diff --git a/packages/SystemUI/res/values-ml/config.xml b/packages/SystemUI/res/values-ml/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ml/config.xml
+++ b/packages/SystemUI/res/values-ml/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 446f0e4..d4d6297 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ക്യാമറ തുറക്കുക"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"പുതിയ ടാസ്ക് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
<string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"വിരലടയാള ഐക്കൺ"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ആപ്പ് ഐക്കൺ"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"സഹായ സന്ദേശ ഏരിയ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"അനുയോജ്യതാ സൂം ബട്ടൺ."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ചെറുതിൽ നിന്ന് വലിയ സ്ക്രീനിലേക്ക് സൂം ചെയ്യുക."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ബ്ലൂടൂത്ത് കണക്റ്റുചെയ്തു."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"വൈഫൈ ഓഫുചെയ്യുക"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"വൈഫൈ ഓണാണ്"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"വൈഫൈ നെറ്റ്വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"കാസ്റ്റുചെയ്യുക"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"കാസ്റ്റുചെയ്യുന്നു"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"പേരിടാത്ത ഉപകരണം"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"കണക്റ്റുചെയ്യുന്നു..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ടെതറിംഗ്"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ഹോട്ട്സ്പോട്ട്"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ഓൺ ചെയ്യുന്നു..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d ഉപകരണങ്ങൾ</item>
+ <item quantity="one">%d ഉപകരണം</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"അറിയിപ്പുകൾ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ടോർച്ച്"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"മൊബൈൽ ഡാറ്റ"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ഉപയോഗിച്ചു"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> പരിധി"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> മുന്നറിയിപ്പ്"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"നൈറ്റ് ലൈറ്റ്"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"സൂര്യാസ്തമയത്തിന്"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"സൂര്യോദയം വരെ"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"സുരക്ഷിത മോഡിൽ <xliff:g id="APP">%s</xliff:g> പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"എല്ലാം മായ്ക്കുക"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"സ്പ്ലിറ്റ് സ്ക്രീൻ ഉപയോഗിക്കുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ആപ്പുകൾ മാറാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"തിരശ്ചീനമായി വേർതിരിക്കുക"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ലംബമായി വേർതിരിക്കുക"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ഇഷ്ടാനുസൃതമായി വേർതിരിക്കുക"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ഇപ്പോൾ ഓഫ് ചെയ്യുക"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"വികസിപ്പിക്കുക"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ചുരുക്കുക"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"സ്ക്രീൻ പിൻ ചെയ്തു"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തും. അൺപിൻ ചെയ്യാൻ \'തിരികെ\', \'ചുരുക്കവിവരണം\' എന്നിവ സ്പർശിച്ച് പിടിക്കുക."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തും. അൺപിൻ ചെയ്യാൻ \'ചുരുക്കവിവരണം\' സ്പർശിച്ച് പിടിക്കുക."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"മനസ്സിലായി"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"വേണ്ട, നന്ദി"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> എന്നത് മറയ്ക്കണോ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"അടുത്ത തവണ നിങ്ങൾ അത് ക്രമീകരണങ്ങളിൽ ഓണാക്കുമ്പോൾ അത് വീണ്ടും ദൃശ്യമാകും."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"മറയ്ക്കുക"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"പവർ അറിയിപ്പ് നിയന്ത്രണം ഉപയോഗിച്ച്, ഒരു ആപ്പിനായുള്ള അറിയിപ്പുകൾക്ക് 0 മുതൽ 5 വരെയുള്ള പ്രാധാന്യ ലെവലുകളിലൊന്ന് നിങ്ങൾക്ക് സജ്ജമാക്കാവുന്നതാണ്. \n\n"<b>"ലെവൽ 5"</b>" \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ മുകളിൽ കാണിക്കുക \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം അനുവദിക്കുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 4"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 3"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും സൃശ്യമാക്കരുത് \n\n"<b>"ലെവൽ 2"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n\n"<b>"ലെവൽ 1"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n- ലോക്ക് സ്ക്രീനിൽ നിന്നും സ്റ്റാറ്റസ് ബാറിൽ നിന്നും മറയ്ക്കുക \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ അടിയിൽ കാണിക്കുക \n\n"<b>"ലെവൽ 0"</b>" \n- ആപ്പിൽ നിന്നുള്ള എല്ലാ അറിയിപ്പുകളും ബ്ലോക്കുചെയ്യുക"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"അറിയിപ്പുകൾ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"നിങ്ങൾ ഇനി ഈ അറിയിപ്പുകൾ കാണില്ല"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"സാധാരണയായി നിങ്ങൾ ഈ അറിയിപ്പുകൾ നിരാകരിക്കുന്നു. \nഅവ തുടർന്നും കാണിക്കണോ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ഈ അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"അറിയിപ്പുകൾ നിർത്തുക"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"തുടർന്നും കാണിക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn/config.xml b/packages/SystemUI/res/values-mn/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-mn/config.xml
+++ b/packages/SystemUI/res/values-mn/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/config.xml b/packages/SystemUI/res/values-mr/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-mr/config.xml
+++ b/packages/SystemUI/res/values-mr/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 804e916..c348110 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"कॅमेरा उघडा"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नवीन कार्य लेआउट निवडा"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिंट आयकन"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"अॅप्लिकेशन आयकन"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"मदत संदेश क्षेत्र"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"सुसंगतता झूम बटण."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"लहानपासून मोठ्या स्क्रीनवर झूम करा."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लूटूथ कनेक्ट केले."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"वाय-फाय बंद"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"वाय-फाय चालू"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"वाय-फाय नेटवर्क उपलब्ध नाहीत"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"कास्ट करा"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"कास्ट करत आहे"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"निनावी डिव्हाइस"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"कनेक्ट करत आहे..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"टेदरिंग"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"हॉटस्पॉट"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"चालू करत आहे…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d डिव्हाइस</item>
+ <item quantity="other">%d डिव्हाइस</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचना"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"फ्लॅशलाइट"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"मोबाइल डेटा"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> वापरले"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> मर्यादा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावणी"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"कार्य प्रोफाइल"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"सूचना आणि अॅप्स बंद आहेत"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"रात्रीचा प्रकाश"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"संध्याकाळी चालू असते"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"सूर्योदयापर्यंत"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> सुरक्षित-मोडमध्ये अक्षम केला आहे."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"सर्व साफ करा"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"विभाजित स्क्रीन वापर करण्यासाठी येथे ड्रॅग करा"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"अॅप्स स्विच करण्यासाठी वर स्वाइप करा"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज विभाजित करा"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"अनुलंब विभाजित करा"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"सानुकूल विभाजित करा"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"आता बंद करा"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तृत करा"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"संकुचित करा"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"स्क्रीन पिन केलेली आहे"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"आपण अनपिन करेर्यंत हे यास दृश्यामध्ये ठेवते. अनपिन करण्यासाठी परत आणि विहंगावलोकनास स्पर्श करा आणि धरून ठेवा."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"आपण अनपिन करेर्यंत हे यास दृश्यामध्ये ठेवते. अनपिन करण्यासाठी विहंगावलोकनास स्पर्श करा आणि धरून ठेवा."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"समजले"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"नाही, नको"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> लपवायचे?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"आपण सेटिंग्जमध्ये ते पुढील वेळी चालू कराल तेव्हा ते पुन्हा दिसेल."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"लपवा"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- पूर्ण स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n\n"<b>"स्तर 1"</b>\n"- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"आता तुम्हाला या सूचना दिसणार नाहीत"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"तुम्ही या सूचना सामान्यतः डिसमिस करता. \nते दाखवत राहायचे?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"या सूचना दाखवणे सुरू ठेवायचे?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"सूचना थांबवा"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"दाखवणे सुरू ठेवा"</string>
diff --git a/packages/SystemUI/res/values-ms/config.xml b/packages/SystemUI/res/values-ms/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ms/config.xml
+++ b/packages/SystemUI/res/values-ms/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-my/config.xml b/packages/SystemUI/res/values-my/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-my/config.xml
+++ b/packages/SystemUI/res/values-my/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 972f4ee..41f28e2 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
<string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"လက်ဗွေ သင်္ကေတ"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"အပလီကေးရှင်း သင်္ကေတ"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"အကူအညီမက်ဆေ့ဂျ် နေရာ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"အံဝင်ခွင်ကျ ဇူးမ်ခလုတ်"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ဖန်သားပြင်ပေါ်တွင် အသေးမှအကြီးသို့ ဇူးမ်ဆွဲခြင်း"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ဘလူးတုသ်ချိတ်ဆက်ထားမှု"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ဝိုင်ဖိုင်ပိတ်ရန်"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ကိုဖွင့်ပါ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Wi-Fi ကွန်ရက် မရှိပါ"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"ကာစ်တင်"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"အမည်မတပ် ကိရိယာ"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"ဆက်သွယ်နေ..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"တွဲချီပေးခြင်း"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ဟော့စပေါ့"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ဖွင့်နေသည်..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">စက် %d ခု</item>
+ <item quantity="one">စက် %d ခု</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"အကြောင်းကြားချက်များ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ဖလက်ရှ်မီး"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"မိုဘိုင်းဒေတာ"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ပိတ်ရန်"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"တိုးချဲ့ရန်"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ခေါက်သိမ်းရန်..."</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"အထွက် စက်ပစ္စည်းကို ပြောင်းပါ"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"မျက်နှာပြင် ပင်ထိုးပြီးပါပြီ"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"သင်ပင်မဖြုတ်မခြင်း ၎င်းကို ပြသထားပါမည်။ ပင်ဖြုတ်ရန် Back နှင့် Overview ကို ထိ၍ဖိထားပါ။"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"သင်ပင်မဖြုတ်မချင်း ၎င်းကိုပြသထားပါမည်။ ပင်ဖြုတ်ရန် Overview ကိုထိပြီး ဖိထားပါ။"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ရပါပြီ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"မလိုတော့ပါ"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ဝှက်မည်လား?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"နောက်တစ်ကြိမ်သင် ချိန်ညှိချက်များဖွင့်လျှင် ၎င်းပေါ်လာပါမည်။"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ဖျောက်ထားမည်"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများကိုအသုံးပြုပြီး အက်ပ်တစ်ခု၏ အကြောင်းကြားချက် အရေးပါမှု ၀ မှ ၅ အထိသတ်မှတ်ပေးနိုင်သည်။ \n\n"<b>"အဆင့် ၅"</b>" \n- အကြောင်းကြားချက်စာရင်း၏ ထိပ်ဆုံးတွင် ပြသည် \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်းကို ခွင့်ပြုသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၄"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၃"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n\n"<b>"အဆင့် ၂"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n\n"<b>"အဆင့် ၁"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n- လော့ခ်ချထားသည့် မျက်နှာပြင်နှင့် အခြေအနေဘားတန်းတို့တွင် မပြပါ \n- အကြောင်းကြားချက်စာရင်း အောက်ဆုံးတွင်ပြသည် \n\n"<b>"အဆင့် ၀"</b>" \n- အက်ပ်မှ အကြောင်းကြားချက်များ အားလုံးကို ပိတ်ဆို့သည်"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"အကြောင်းကြားချက်များ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ဤအကြောင်းကြားချက်များကို မြင်ရတော့မည် မဟုတ်ပါ"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"သင်သည် အများအားဖြင့် ဤအကြောင်းကြားချက်များကို ပယ်လေ့ရှိပါသည်။ \n၎င်းတို့ကို ဆက်လက်ပြသလိုပါသလား။"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ဤအကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"အကြောင်းကြားချက်များကို ရပ်ရန်"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ဆက်ပြရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/config.xml b/packages/SystemUI/res/values-nb/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-nb/config.xml
+++ b/packages/SystemUI/res/values-nb/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index d4d6160..5bd4f2e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"åpne kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Velg en ny utforming for oppgaver"</string>
<string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeravtrykk"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Appikon"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Område for hjelpemelding"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoomknapp for kompatibilitet."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom fra mindre til større skjerm."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth er tilkoblet."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi er av"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi er på"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Ingen tilgjengelige Wi-Fi-nettverk"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Enhet uten navn"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Kobler til …"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internettdeling"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Wi-Fi-sone"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Slår på …"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d enheter</item>
+ <item quantity="one">%d enhet</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Varsler"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lommelykt"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobildata"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> brukt"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Grense på <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advarsel for <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Jobbprofil"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Varsler og apper er slått av"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattlys"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"På ved solnedgang"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Til soloppgang"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> er slått av i sikker modus."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tøm alt"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Dra hit for å bruke delt skjerm"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Sveip opp for å bytte apper"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Del horisontalt"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Del vertikalt"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Del tilpasset"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Slå av nå"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Utvid"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skjul"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"Skjermen er låst"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"På denne måten blir skjermen synlig frem til du løsner den. Trykk og hold inne Tilbake og Oversikt for å løsne den."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"På denne måten blir skjermen synlig frem til du løsner den. Trykk og hold inne Oversikt for å løsne den."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Skjønner"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nei takk"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igjen neste gang du slår den på i innstillingene."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Med effektive varselinnstillinger kan du angi viktighetsnivåer fra 0 til 5 for appvarsler. \n\n"<b>"Nivå 5"</b>" \n– Vis øverst på varsellisten \n– Tillat forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 4"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 3"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n\n"<b>"Nivå 2"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri fort \n– Tillat aldri lyder eller vibrering \n\n"<b>"Nivå 1"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n– Tillat aldri lyder eller vibrering \n– Skjul fra låseskjermen og statusfeltet \n– Vis nederst på varsellisten \n\n"<b>"Nivå 0"</b>" \n– Blokkér alle varsler fra appen"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Varsler"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Du ser ikke disse varslene lenger"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Du avviser vanligvis disse varslene. \nVil du fortsette å vise dem?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsette å vise disse varslene?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Stopp varsler"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsett å vise"</string>
diff --git a/packages/SystemUI/res/values-ne/config.xml b/packages/SystemUI/res/values-ne/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ne/config.xml
+++ b/packages/SystemUI/res/values-ne/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e3efe1b..ca81f44 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -41,7 +41,7 @@
<string name="invalid_charger" msgid="4549105996740522523">"USB चार्ज गर्न समर्थित छैन।\n आपूर्ति गरिएको चार्जर मात्र प्रयोग गर्नुहोस्।"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्ज समर्थित छैन।"</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"आपूर्ति गरिएको चार्जर मात्र प्रयोग गर्नुहोस्।"</string>
- <string name="battery_low_why" msgid="4553600287639198111">"सेटिङ्हरू"</string>
+ <string name="battery_low_why" msgid="4553600287639198111">"सेटिङहरू"</string>
<string name="battery_saver_confirmation_title" msgid="2052100465684817154">"ब्याट्री सेभर सक्रिय गर्ने हो?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"खोल्नुहोस्"</string>
<string name="battery_saver_start_action" msgid="8187820911065797519">"ब्याट्री सेभर सक्रिय गर्नुहोस्"</string>
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"क्यामेरा खोल्नुहोस्"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"नयाँ कार्य लेआउट चयन गर्नुहोस्"</string>
<string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिन्ट जनाउने आइकन"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"अनुप्रयोग जनाउने आइकन"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"मद्दतसम्बन्धी सन्देशको क्षेत्र"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"मिलाउने जुम बटन।"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"स्क्रिनलाई सानोबाट ठूलो पार्नुहोस्।"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लुटुथ जडान भयो।"</string>
@@ -264,8 +261,8 @@
<item quantity="other">भित्र थप <xliff:g id="NUMBER_1">%s</xliff:g> सूचनाहरू छन्।</item>
<item quantity="one">भित्र थप <xliff:g id="NUMBER_0">%s</xliff:g> सूचना छ।</item>
</plurals>
- <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"अधिसूचना सेटिङ्हरू"</string>
- <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिङ्हरू"</string>
+ <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"अधिसूचना सेटिङहरू"</string>
+ <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिङहरू"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रिन स्वतः घुम्ने छ।"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"स्क्रिनलाई ल्यान्डस्केप अवस्थामा बन्द गरिएको छ।"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"स्क्रिन पोर्टेट अभिमूखमा लक गरिएको छ।"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi बन्द"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi सक्रिय छ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Wi-Fi नेटवर्क अनुपलब्ध"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"प्रसारण गर्दै"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"बेनाम उपकरण"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"जडान हुँदै..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"टेदर गर्दै"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"हटस्पट"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"सक्रिय गर्दै…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d यन्त्रहरू</item>
+ <item quantity="one">%d यन्त्र</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"अधिसूचनाहरू"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"फ्ल्यासलाइट"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"मोबाइल डेटा"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> प्रयोग गरियो"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी दिँदै"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"कार्य प्रोफाइल"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"सूचना तथा अनुप्रयोगहरू निष्क्रिय छन्"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"रात्रिको प्रकाश"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"सूर्यास्तमा सक्रिय"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"सूर्योदयसम्म"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> लाई सुरक्षित-मोडमा असक्षम गरिएको छ।"</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"सबै हटाउनुहोस्"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"विभाजित स्क्रिनको प्रयोग गर्नका लागि यहाँ तान्नुहोस्"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"अनुप्रयोगहरू बदल्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ठाडो रूपमा विभाजन गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"आफू अनुकूल विभाजन गर्नुहोस्"</string>
@@ -376,11 +374,11 @@
<string name="description_target_search" msgid="3091587249776033139">"खोज्नुहोस्"</string>
<string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>को लागि माथि धिसार्नुहोस्"</string>
<string name="description_direction_left" msgid="7207478719805562165">"स्लाइड <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>को लागि बायाँ।"</string>
- <string name="zen_priority_introduction" msgid="1149025108714420281">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै संगीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
- <string name="zen_alarms_introduction" msgid="4934328096749380201">"तपाईंलाई अलार्महरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै संगीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
+ <string name="zen_priority_introduction" msgid="1149025108714420281">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
+ <string name="zen_alarms_introduction" msgid="4934328096749380201">"तपाईंलाई अलार्महरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
<string name="zen_priority_customize_button" msgid="7948043278226955063">"आफू अनुकूल बनाउनुहोस्"</string>
- <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"यसले अलार्म, संगीत, भिडियो, र खेलहरू लगायत सबैका ध्वनि र कम्पनहरूमाथि रोक लगाउँछ। तपाईं अझै पनि फोन कलहरू गर्न सक्नुहुनेछ।"</string>
- <string name="zen_silence_introduction" msgid="3137882381093271568">"यसले अलार्म, संगीत, भिडियोहरू र खेलहरूसहित सबै ध्वनिहरू र कम्पनहरूलाई रोक्छ।"</string>
+ <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"यसले अलार्म, सङ्गीत, भिडियो, र खेलहरू लगायत सबैका ध्वनि र कम्पनहरूमाथि रोक लगाउँछ। तपाईं अझै पनि फोन कलहरू गर्न सक्नुहुनेछ।"</string>
+ <string name="zen_silence_introduction" msgid="3137882381093271568">"यसले अलार्म, सङ्गीत, भिडियोहरू र खेलहरूसहित सबै ध्वनिहरू र कम्पनहरूलाई रोक्छ।"</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"तल कम जरुरी सूचनाहरू"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"अहिले नै निष्क्रिय पार्नुहोस्"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तार गर्नुहोस्"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"संक्षिप्त पार्नुहोस्"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"पर्दा राखेका छ"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"बुझेँ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"धन्यवाद पर्दैन"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"लुकाउनुहुन्छ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"यो तपाईं सेटिङ् मा यो बारी अर्को समय देखापर्नेछ।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"लुकाउनुहोस्"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- अनुप्रयोगका सबै सूचनाहरूलाई रोक्ने"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचनाहरू"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"तपाईं अब उप्रान्त यी सूचनाहरू देख्नु हुने छैन"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"तपाईं सामान्यतया यी सूचनाहरूलाई खारेज गर्ने गर्नुहुन्छ। \nतिनलाई देखाइरहने हो?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"यी सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"सूचनाहरू देखाउन छाड्नुहोस्"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"देखाउने क्रम जारी राख्नुहोस्"</string>
@@ -656,7 +667,7 @@
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"सम्पर्कहरू"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"इमेल"</string>
<string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
- <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"सङ्गीत"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"पात्रो"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"भोल्युम नियन्त्रणसहित देखाउनुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/config.xml b/packages/SystemUI/res/values-nl/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-nl/config.xml
+++ b/packages/SystemUI/res/values-nl/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/config.xml b/packages/SystemUI/res/values-pa/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-pa/config.xml
+++ b/packages/SystemUI/res/values-pa/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 4e39106..9308fee 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"ਕੈਮਰਾ ਖੋਲ੍ਹੋ"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"ਨਵਾਂ ਕੰਮ ਲੇਆਉਟ ਚੁਣੋ"</string>
<string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਪ੍ਰਤੀਕ"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਪ੍ਰਤੀਕ"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"ਮਦਦ ਸੁਨੇਹਾ ਖੇਤਰ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ਅਨੁਰੂਪਤਾ ਜ਼ੂਮ ਬਟਨ।"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ਵੱਡੀ ਸਕ੍ਰੀਨ ਤੇ ਛੋਟਾ ਜ਼ੂਮ ਕਰੋ।"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ਕਨੈਕਟ ਕੀਤੀ।"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ਵਾਈ-ਫਾਈ ਬੰਦ"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"ਵਾਈ-ਫਾਈ ਚਾਲੂ"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"ਕੋਈ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"ਕਾਸਟ"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"ਕਾਸਟਿੰਗ"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ਬਿਨਾਂ ਨਾਮ ਦਾ ਡੀਵਾਈਸ"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ਟੈਦਰਿੰਗ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ਹੌਟਸਪੌਟ"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d ਡੀਵਾਈਸ</item>
+ <item quantity="other">%d ਡੀਵਾਈਸਾਂ</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ਸੂਚਨਾਵਾਂ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ਫਲੈਸ਼ਲਾਈਟ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ਵਰਤਿਆ"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਸੀਮਾ"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਚਿਤਾਵਨੀ"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"ਰਾਤਰੀ ਲਾਈਟ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"ਸੂਰਜ ਛਿਪਣ \'ਤੇ ਚਾਲੂ"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਸੁਰੱਖਿਅਤ-ਮੋਡ ਵਿੱਚ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ।"</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ਹੌਰੀਜ਼ੌਂਟਲ ਸਪਲਿਟ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ਵਰਟੀਕਲ ਸਪਲਿਟ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ਵਿਉਂਂਤੀ ਸਪਲਿਟ"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ਹੁਣੇ ਬੰਦ ਕਰੋ"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"ਨਸ਼ਟ ਕਰੋ"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"ਸਕ੍ਰੀਨ ਪਿੰਨ ਕੀਤੀ"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"ਇਹ ਇਸ ਨੂੰ ਤਦ ਤੱਕ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਰੱਖਦਾ ਹੈ ਜਦ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿੰਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿੰਨ ਕਰਨ ਲਈ \'ਪਿੱਛੇ\' ਅਤੇ \'ਰੂਪ-ਰੇਖਾ\' ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"ਇਹ ਇਸ ਨੂੰ ਤਦ ਤੱਕ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਰੱਖਦਾ ਹੈ ਜਦ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿੰਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿੰਨ ਕਰਨ ਲਈ \'ਰੂਪ-ਰੇਖਾ\' ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ਸਮਝ ਲਿਆ"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ਕੀ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ਨੂੰ ਲੁਕਾਉਣਾ ਹੈ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ਇਹ ਅਗਲੀ ਵਾਰ ਮੁੜ ਪ੍ਰਗਟ ਹੋਵੇਗਾ ਜਦੋਂ ਤੁਸੀਂ ਇਸਨੂੰ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਾਲੂ ਕਰਦੇ ਹੋ।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ਲੁਕਾਓ"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"ਪਾਵਰ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨਾਲ, ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਲਈ ਮਹੱਤਤਾ ਪੱਧਰ ਨੂੰ 0 ਤੋਂ 5 ਤੱਕ ਸੈੱਟ ਕਰ ਸਕਦੇ ਹੋ। \n\n"<b>"ਪੱਧਰ 5"</b>" \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਦਿਖਾਓ \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਦੀ ਆਗਿਆ ਦਿਓ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 4"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 3"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 2"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਵੀ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n\n"<b>"ਪੱਧਰ 1"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n- ਲਾਕ ਸਕ੍ਰੀਨ ਅਤੇ ਸਥਿਤੀ ਪੱਟੀ ਤੋਂ ਲੁਕਾਓ \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਹੇਠਾਂ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 0"</b>" \n- ਐਪ ਤੋਂ ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕਰੋ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ਸੂਚਨਾਵਾਂ"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ਤੁਹਾਨੂੰ ਹੁਣ ਇਹ ਸੂਚਨਾਵਾਂ ਦਿਖਾਈ ਨਹੀਂ ਦੇਣਗੀਆਂ"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"ਤੁਸੀਂ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ ਖਾਰਜ ਕਰਦੇ ਹੋ। \nਕੀ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ਕੀ ਇਨ੍ਹਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/config.xml b/packages/SystemUI/res/values-pl/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-pl/config.xml
+++ b/packages/SystemUI/res/values-pl/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/config.xml b/packages/SystemUI/res/values-pt-rBR/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-pt-rBR/config.xml
+++ b/packages/SystemUI/res/values-pt-rBR/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3472d1d..982b8b7 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícone do app"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área da mensagem de ajuda"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado."</string>
@@ -313,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desligado"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ativado"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nenhuma rede Wi-Fi disponível"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmitir"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Transmitindo"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
@@ -329,9 +328,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Conectando..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ponto de acesso"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Ativando…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d dispositivo</item>
+ <item quantity="other">%d dispositivos</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dados móveis"</string>
@@ -501,11 +502,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Desativar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Recolher"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Alterar dispositivo de saída"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"A tela está fixada"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ela reaparecerá na próxima vez que você ativá-la nas configurações."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
@@ -585,8 +599,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Geralmente você dispensa essas notificações. \nQuer que elas continuem a ser exibidas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/config.xml b/packages/SystemUI/res/values-pt-rPT/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-pt-rPT/config.xml
+++ b/packages/SystemUI/res/values-pt-rPT/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a631fcc..2bc1c03 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"abrir câmara"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo esquema de tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícone de aplicação"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área da mensagem de ajuda"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão zoom de compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom menor para ecrã maior."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ligado."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Desligado"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ligado"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Não estão disponíveis redes Wi-Fi"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmitir"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Transmissão"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"A ligar..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Associação"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"A ativar…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d dispositivos</item>
+ <item quantity="one">%d dispositivo</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dados móveis"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> utilizado(s)"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Limite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Perfil de trabalho"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"As notificações e as aplicações estão desativadas"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz noturna"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Ativ. ao pôr-do-sol"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Até ao nascer do sol"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"O <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Arraste aqui para utilizar o ecrã dividido"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Deslizar rapidamente para cima para mudar de aplicação"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
@@ -502,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Desativar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Reduzir"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Mudar de dispositivo de saída"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"O ecrã está fixado"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Esta opção mantém o item visível até o soltar. Toque sem soltar em Anterior e em Vista geral para soltar."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Esta opção mantém o item visível até o soltar. Toque sem soltar em Vista geral para soltar."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Compreendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Pretende ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Reaparecerá da próxima vez que a funcionalidade for ativada nas definições."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
@@ -586,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Com os controlos de notificações do consumo de energia, pode definir um nível de importância de 0 a 5 para as notificações de aplicações. \n\n"<b>"Nível 5"</b>" \n- Mostrar no início da lista de notificações \n- Permitir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre \n\n"<b>"Nível 4"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre\n\n"<b>"Nível 3"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n\n"<b>"Nível 2"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n- Ocultar do ecrã de bloqueio e da barra de estado \n- Mostrar no fim da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações da aplicação"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nunca mais verá estas notificações."</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Normalmente, ignora estas notificações. \nPretende continuar a mostrá-las?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Pretende continuar a ver estas notificações?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Parar notificações"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar a mostrar"</string>
diff --git a/packages/SystemUI/res/values-pt/config.xml b/packages/SystemUI/res/values-pt/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-pt/config.xml
+++ b/packages/SystemUI/res/values-pt/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3472d1d..982b8b7 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
<string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ícone do app"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Área da mensagem de ajuda"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado."</string>
@@ -313,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desligado"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ativado"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nenhuma rede Wi-Fi disponível"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmitir"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Transmitindo"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
@@ -329,9 +328,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Conectando..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ponto de acesso"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Ativando…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d dispositivo</item>
+ <item quantity="other">%d dispositivos</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dados móveis"</string>
@@ -501,11 +502,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Desativar agora"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Recolher"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Alterar dispositivo de saída"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"A tela está fixada"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ela reaparecerá na próxima vez que você ativá-la nas configurações."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
@@ -585,8 +599,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Geralmente você dispensa essas notificações. \nQuer que elas continuem a ser exibidas?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
diff --git a/packages/SystemUI/res/values-ro/config.xml b/packages/SystemUI/res/values-ro/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ro/config.xml
+++ b/packages/SystemUI/res/values-ro/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 58b6b75..d765282 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -104,12 +104,9 @@
<string name="camera_label" msgid="7261107956054836961">"deschideți camera foto"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Selectați noul aspect pentru activitate"</string>
<string name="cancel" msgid="6442560571259935130">"Anulați"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pictograma amprentă"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Pictograma aplicației"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Zona mesajelor de ajutor"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceți zoom de la o imagine mai mică la una mai mare."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Conectat prin Bluetooth."</string>
@@ -315,6 +312,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi deconectat"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi activat"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nicio rețea Wi-Fi disponibilă"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Proiectați"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Se proiectează"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispozitiv nedenumit"</string>
@@ -331,9 +330,12 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Se conectează..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Se activează..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="few">%d dispozitive</item>
+ <item quantity="other">%d de dispozitive</item>
+ <item quantity="one">%d dispozitiv</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificări"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanternă"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Date mobile"</string>
@@ -503,11 +505,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Dezactivați acum"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Extindeți"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Restrângeți"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Comutați dispozitivul de ieșire"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Ecranul este fixat"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunile Înapoi și Recente pentru a anula fixarea."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunea Recente pentru a anula fixarea."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Am înțeles"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nu, mulțumesc"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ascundeți <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Va reapărea la următoarea activare în setări."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ascundeți"</string>
@@ -587,8 +602,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificări"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Nu veți mai vedea aceste notificări"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"De regulă respingeți aceste notificări. \nDoriți să fie afișate în continuare?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Doriți să continuați afișarea acestor notificări?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Opriți notificările"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuați afișarea"</string>
diff --git a/packages/SystemUI/res/values-ru/config.xml b/packages/SystemUI/res/values-ru/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ru/config.xml
+++ b/packages/SystemUI/res/values-ru/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3bda5eb..9cd9082 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -105,12 +105,9 @@
<string name="camera_label" msgid="7261107956054836961">"Открыть камеру."</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Выберите другой макет"</string>
<string name="cancel" msgid="6442560571259935130">"Отмена"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок отпечатка пальца"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Значок приложения"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Справочное сообщение"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-соединение установлено."</string>
@@ -317,6 +314,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi выкл."</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi включен"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Не удалось найти доступные сети Wi-Fi"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Трансляция"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Передача изображения"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Безымянное устройство"</string>
@@ -333,9 +332,13 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Соединение..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доступа"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Подождите…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d устройство</item>
+ <item quantity="few">%d устройства</item>
+ <item quantity="many">%d устройств</item>
+ <item quantity="other">%d устройства</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Уведомления"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Фонарик"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобильный Интернет"</string>
@@ -345,10 +348,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Использовано: <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ограничение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Рабочий профиль"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Уведомления и приложения отключены"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ночной режим"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Включить на закате"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"До рассвета"</string>
@@ -366,8 +367,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" отключено в безопасном режиме."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Очистить все"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Перетащите сюда, чтобы разделить экран"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Чтобы переключиться между приложениями, проведите по экрану вверх."</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Разделить по горизонтали"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Разделить по вертикали"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Разделить по-другому"</string>
@@ -508,11 +508,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Отключить"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Развернуть"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Свернуть"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Сменить устройство аудиовыхода"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Блокировка в приложении включена"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Приложение останется активным, пока вы не отмените блокировку, нажав и удерживая кнопки \"Назад\" и \"Обзор\"."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Приложение останется активным, пока вы не отмените блокировку, нажав и удерживая кнопку \"Обзор\"."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"ОК"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Нет, спасибо"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Скрыть параметр \"<xliff:g id="TILE_LABEL">%1$s</xliff:g>\"?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Этот параметр появится в следующий раз, когда вы включите его."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Скрыть"</string>
@@ -592,8 +605,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"С помощью этой функции вы можете устанавливать уровень важности уведомлений от 0 до 5 для каждого приложения.\n\n"<b>"Уровень 5"</b>\n"‒ Помещать уведомления в начало списка.\n‒ Показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 4\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 3\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\nУровень 2\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\nУровень 1\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\n‒ Не показывать на экране блокировки и в строке состояния.\n‒ Помещать уведомления в конец списка.\nУровень 0\n"<b></b>\n"‒ Блокировать все уведомления приложения."</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Уведомления"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Вы больше не будете получать эти уведомления."</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Обычно вы скрываете эти уведомления.\nПоказывать их?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Показывать эти уведомления?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Отключить уведомления"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Показывать"</string>
diff --git a/packages/SystemUI/res/values-si/config.xml b/packages/SystemUI/res/values-si/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-si/config.xml
+++ b/packages/SystemUI/res/values-si/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8c9208b..b041d48 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"කැමරාව විවෘත කරන්න"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"නව කාර්යය සැකැස්ම තෝරන්න"</string>
<string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ඇඟිලි සලකුණු නිරූපකය"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"යෙදුම් නිරූපකය"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"උදවු පණිවිඩ ප්රදේශය"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ගැළපෙන විශාලන බොත්තම."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"විශාල තිරය වෙත කුඩාව විශාලනය කරන්න."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"බ්ලූටූත් සම්බන්ධිතයි."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi අක්රියයි"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ක්රියාත්මකයි"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Wi-Fi ජාල ලබා ගත නොහැකිය"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"කාස්ට් කිරීම"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"නම් නොකළ උපාංගය"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"සම්බන්ධ වෙමින්..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ටෙදරින්"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"හොට්ස්පොට්"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ක්රියාත්මක කරමින්…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">උපාංග %d</item>
+ <item quantity="other">උපාංග %d</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"දැනුම්දීම්"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"සැණෙළි ආලෝකය"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ජංගම දත්ත"</string>
@@ -499,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"දැන් ක්රියාවිරහිත කරන්න"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"දිග හරින්න"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"හකුළන්න"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"ප්රතිදාන උපාංගය මාරු කරන්න"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"තීරය අමුණන ලදි"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"මෙය ඔබ ගලවන තෙක් එය දසුන තුළ තබයි. ගැලවීමට දළ විශ්ලේෂණය ස්පර්ශ කර ආපසු අල්ලාගෙන සිටින්න."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"මෙය ඔබ ගලවන තෙක් එය දසුන තුළ තබයි. ගැලවීමට දළ විශ්ලේෂණය ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"හරි, තේරුණා"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"එපා ස්තූතියි"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> සඟවන්නද?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ඊළඟ අවස්ථාවේ සැකසීම් තුළ ඔබ එය සක්රිය කළ විට එය නැවත දිසිවේ."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"සඟවන්න"</string>
@@ -583,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"බල දැනුම්දීම් පාලන සමගින්, ඔබට යෙදුමක දැනුම්දීම් සඳහා වැදගත්කම 0 සිට 5 දක්වා සැකසිය හැකිය. \n\n"<b>"5 මට්ටම"</b>" \n- දැනුම්දීම් ලැයිස්තුවේ ඉහළින්ම පෙන්වන්න \n- පූර්ණ තිර බාධාවට ඉඩ දෙන්න \n- සැම විට එබී බලන්න \n\n"<b>"4 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- සැම විට එබී බලන්න \n\n"<b>"3 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n\n"<b>"2 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n\n"<b>"1 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n- අගුලු තිරය සහ තත්ත්ව තීරුව වෙතින් සඟවන්න \n- දැනුම්දීම් ලැයිස්තුවේ පහළින්ම පෙන්වන්න \n\n"<b>"0 මට්ටම"</b>" \n- යෙදුම වෙතින් වන සියලු දැනුම් දීම් සඟවන්න."</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"දැනුම් දීම්"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ඔබට තවදුරටත් මෙම දැනුම්දීම් නොදකිනු ඇත"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"ඔබ සාමාන්යයෙන් මෙවැනි දැනුම්දීම් ඉවත දමයි. \nඒවා දිගටම පෙන්වන්නද?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"මෙම දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"දැනුම්දීම් නවත්වන්න"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"පෙන්වමින් තබන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/config.xml b/packages/SystemUI/res/values-sk/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-sk/config.xml
+++ b/packages/SystemUI/res/values-sk/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/config.xml b/packages/SystemUI/res/values-sl/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-sl/config.xml
+++ b/packages/SystemUI/res/values-sl/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/config.xml b/packages/SystemUI/res/values-sq/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-sq/config.xml
+++ b/packages/SystemUI/res/values-sq/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/config.xml b/packages/SystemUI/res/values-sr/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-sr/config.xml
+++ b/packages/SystemUI/res/values-sr/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/config.xml b/packages/SystemUI/res/values-sv/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-sv/config.xml
+++ b/packages/SystemUI/res/values-sv/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/config.xml b/packages/SystemUI/res/values-sw/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-sw/config.xml
+++ b/packages/SystemUI/res/values-sw/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 3880540..a74f8a4 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"fungua kamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Chagua muundo mpya wa kazi"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Aikoni ya alama ya kidole"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Aikoni ya programu"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Sehemu ya ujumbe wa usaidizi"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth imeunganishwa."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Imezimwa"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Imewasha Wi-Fi"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Hakuna mitandao ya Wi-Fi inayopatikana"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Tuma"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Inatuma"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Kifaa hakina jina"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Inaunganisha..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Kusambaza mtandao"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Mtandao-hewa"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Inawasha..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">Vifaa %d</item>
+ <item quantity="one">Kifaa %d</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Arifa"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Tochi"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Data ya mtandao wa simu"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> imetumika"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"kikomo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Onyo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Wasifu wa kazini"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Umezima kipengele cha arifa na programu"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Mwanga wa Usiku"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Itawashwa machweo"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Hadi macheo"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> imezimwa katika hali salama."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Futa zote"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Buruta hapa ili utumie skrini iliyogawanywa"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Telezesha kidole juu ili ubadilishe programu"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gawanya Mlalo"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Gawanya Wima"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Maalum Iliyogawanywa"</string>
@@ -502,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Izime sasa"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Panua"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Kunja"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Badilisha kifaa cha kutoa sauti"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skrini imebandikwa"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Hali hii huifanya ionekane hadi utakapoibandua. Gusa na ushikilie kipengele cha Nyuma na Muhtasari ili ubandue."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Hali hii huifanya ionekane hadi utakapoibandua. Gusa na ushikilie kipengele cha Muhtasari ili ubandue."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Nimeelewa"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Hapana, asante"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ungependa kuficha <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Itaonekana tena wakati mwingine utakapoiwasha katika mipangilio."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ficha"</string>
@@ -586,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Ukiwa na udhibiti wa arifa, unaweza kuweka kiwango cha umuhimu wa arifa za programu kuanzia 0 hadi 5. \n\n"<b>"Kiwango cha 5"</b>" \n- Onyesha katika sehemu ya juu ya orodha ya arifa \n- Ruhusu ukatizaji wa skrini nzima \n- Ruhusu arifa za kuchungulia kila wakati\n\n"<b>"Kiwango cha 4"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Ruhusu arifa za kuchungulia kila wakati \n\n"<b>"Kiwango cha 3"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia\n\n"<b>"Kiwango cha 2"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti au mtetemo \n\n"<b>"Kiwango cha 1"</b>" \n- Zuia ukatizaji wa skrini nzima \n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti na mtetemo \n- Usionyeshe skrini iliyofungwa na sehemu ya arifa \n- Onyesha katika sehemu ya chini ya orodha ya arifa \n\n"<b>"Kiwango cha 0"</b>" \n- Zuia arifa zote kutoka programu"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Arifa"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Hutaona tena arifa hizi"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Wewe huondoa arifa hizi. \nUngependa kuzionyesha?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Ungependa kuendelea kuonyesha arifa hizi?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Acha kuonyesha arifa"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Endelea kuonyesha"</string>
diff --git a/packages/SystemUI/res/values-ta/config.xml b/packages/SystemUI/res/values-ta/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ta/config.xml
+++ b/packages/SystemUI/res/values-ta/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 774b2f2..c8ff5a1 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"கேமராவைத் திற"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"புதிய பணி தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
<string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"கைரேகை ஐகான்"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"பயன்பாட்டு ஐகான்"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"உதவிச் செய்திக்கான பகுதி"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"பொருந்துமாறு அளவை மாற்றும் பொத்தான்."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"சிறியதிலிருந்து பெரிய திரைக்கு அளவை மாற்றும்."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"புளூடூத் இணைக்கப்பட்டது."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"வைஃபையை முடக்கு"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"வைஃபை இயக்கத்தில்"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"வைஃபை நெட்வொர்க்குகள் இல்லை"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"அனுப்புகிறது"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"பெயரிடப்படாத சாதனம்"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"இணைக்கிறது..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"டெதெரிங்"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ஹாட்ஸ்பாட்"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ஆன் செய்கிறது..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d சாதனங்கள்</item>
+ <item quantity="one">%d சாதனம்</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"அறிவிப்புகள்"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"டார்ச் லைட்"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"மொபைல் டேட்டா"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"பயன்படுத்தியது - <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> வரம்பு"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> எச்சரிக்கை"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"பணிச் சுயவிவரம்"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"அறிவிப்புகள் & ஆப்ஸ் ஆஃப் செய்யப்பட்டுள்ளன"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"இரவு ஒளி"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"மாலையில் ஆன் செய்"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"சூரிய உதயம் வரை"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"பாதுகாப்புப் பயன்முறையில் <xliff:g id="APP">%s</xliff:g> முடக்கப்பட்டது."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"அனைத்தையும் அழி"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"திரைப் பிரிப்பைப் பயன்படுத்த, இங்கே இழுக்கவும்"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ஆப்ஸிற்கு இடையே மாற்றுவதற்கு, மேல்நோக்கி ஸ்வைப் செய்க"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"கிடைமட்டமாகப் பிரி"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"செங்குத்தாகப் பிரி"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"தனிவிருப்பத்தில் பிரி"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"இப்போதே முடக்கு"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"விரிவாக்கு"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"சுருக்கு"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"திரை பொருத்தப்பட்டது"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"பொருத்தியதை அகற்றும் வரை இதைக் காட்சியில் வைக்கும். அகற்ற, முந்தையது மற்றும் மேலோட்டப் பார்வையைத் தொட்டுப் பிடிக்கவும்."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"பொருத்தியதை அகற்றும் வரை இதைக் காட்சியில் வைக்கும். அகற்ற, மேலோட்டப் பார்வையைத் தொட்டுப் பிடிக்கவும்."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"புரிந்தது"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"வேண்டாம்"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ஐ மறைக்கவா?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"அடுத்த முறை அமைப்புகளில் மீண்டும் இயக்கும்போது, இது மீண்டும் தோன்றும்."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"மறை"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"ஆற்றல்மிக்க அறிவிப்புக் கட்டுப்பாடுகள் மூலம், பயன்பாட்டின் அறிவிப்புகளுக்கு முக்கியத்துவ நிலையை (0-5) அமைக்கலாம். \n\n"<b>"நிலை 5"</b>" \n- அறிவிப்புப் பட்டியலின் மேலே காட்டும் \n- முழுத் திரைக் குறுக்கீட்டை அனுமதிக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 4"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 3"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n\n"<b>"நிலை 2"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது, அதிர்வுறாது \n\n"<b>"நிலை 1"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது அல்லது அதிர்வுறாது \n- பூட்டுத்திரை மற்றும் நிலைப்பட்டியிலிருந்து மறைக்கும் \n- அறிவிப்புகள் பட்டியலின் கீழே காட்டும் \n\n"<b>"நிலை 0"</b>" \n- பயன்பாட்டின் எல்லா அறிவிப்புகளையும் தடுக்கும்"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"அறிவிப்புகள்"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"இந்த அறிவிப்புகளை இனி பார்க்கமாட்டீர்கள்"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"வழக்கமாக, இந்த அறிவிப்புகளை நிராகரிக்கிறீர்கள். \nதொடர்ந்து இவற்றைக் காட்டலாமா?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"இந்த அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"அறிவிப்புகளை நிறுத்து"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"அறிவிப்புகளைத் தொடர்ந்து காட்டு"</string>
diff --git a/packages/SystemUI/res/values-te/config.xml b/packages/SystemUI/res/values-te/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-te/config.xml
+++ b/packages/SystemUI/res/values-te/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index e143b90..292fddd 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"కెమెరాను తెరువు"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"కొత్త విధి లేఅవుట్ను ఎంచుకోండి"</string>
<string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"వేలిముద్ర చిహ్నం"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"అప్లికేషన్ చిహ్నం"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"సహాయ సందేశ ప్రాంతం"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"అనుకూలత జూమ్ బటన్."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"చిన్న స్క్రీన్ నుండి పెద్దదానికి జూమ్ చేయండి."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"బ్లూటూత్ కనెక్ట్ చేయబడింది."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ఆఫ్లో ఉంది"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi ఆన్లో ఉంది"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Wi-Fi నెట్వర్క్లు ఏవీ అందుబాటులో లేవు"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"ప్రసారం చేయండి"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"ప్రసారం చేస్తోంది"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"పేరులేని పరికరం"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"కనెక్ట్ అవుతోంది..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"టీథరింగ్"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"హాట్స్పాట్"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"ఆన్ చేస్తోంది…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d పరికరాలు</item>
+ <item quantity="one">%d పరికరం</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"నోటిఫికేషన్లు"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ఫ్లాష్లైట్"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"మొబైల్ డేటా"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> వినియోగించబడింది"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> పరిమితి"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> హెచ్చరిక"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"కార్యాలయ ప్రొఫైల్"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"నోటిఫికేషన్లు & యాప్లు ఆఫ్ చేయి"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"రాత్రి కాంతి"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"సూర్యాస్తమయానికి"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"సూర్యోదయం వరకు"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> సురక్షిత-మోడ్లో నిలిపివేయబడింది."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"అన్నీ తీసివేయి"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"విభజన స్క్రీన్ను ఉపయోగించడానికి ఇక్కడ లాగండి"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"యాప్లను మార్చడం కోసం ఎగువకు స్వైప్ చేయండి"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"సమతలంగా విభజించు"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"లంబంగా విభజించు"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"అనుకూలంగా విభజించు"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ఇప్పుడు ఆఫ్ చేయండి"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"విస్తరింపజేయండి"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"కుదించండి"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"స్క్రీన్ పిన్ చేయబడింది"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"దీని వలన మీరు అన్పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్పిన్ చేయడానికి వెనుకకు మరియు స్థూలదృష్టి తాకి & అలాగే పట్టుకోండి."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"దీని వలన మీరు అన్పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్పిన్ చేయడానికి స్థూలదృష్టిని తాకి & అలాగే పట్టుకోండి."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"అర్థమైంది"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"వద్దు, ధన్యవాదాలు"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ని దాచాలా?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"మీరు సెట్టింగ్ల్లో దీన్ని ఆన్ చేసిన తదుపరిసారి ఇది కనిపిస్తుంది."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"దాచు"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ జాబితా పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ జాబితా దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్లు బ్లాక్ చేయబడతాయి"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"నోటిఫికేషన్లు"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"ఇకపై మీకు ఈ నోటిఫికేషన్లు కనిపించవు"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"మీరు సాధారణంగా ఈ నోటిఫికేషన్లను విస్మరిస్తారు. \nవాటి ప్రదర్శనను కొనసాగించాలా?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"ఈ నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"నోటిఫికేషన్లను ఆపివేయి"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"చూపిస్తూనే ఉండు"</string>
diff --git a/packages/SystemUI/res/values-th/config.xml b/packages/SystemUI/res/values-th/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-th/config.xml
+++ b/packages/SystemUI/res/values-th/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/config.xml b/packages/SystemUI/res/values-tl/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-tl/config.xml
+++ b/packages/SystemUI/res/values-tl/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/config.xml b/packages/SystemUI/res/values-tr/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-tr/config.xml
+++ b/packages/SystemUI/res/values-tr/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 8a289be..9adc141 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"kamerayı aç"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Yeni görev düzenini seçin"</string>
<string name="cancel" msgid="6442560571259935130">"İptal"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Parmak izi simgesi"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Uygulama simgesi"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Yardım mesajı alanı"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth bağlandı."</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Kablosuz Kapalı"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Kablosuz Bağlantı Açık"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Kullanılabilir kablosuz ağ yok"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Yayınla"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Yayınlanıyor"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Adsız cihaz"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Bağlanılıyor..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Açılıyor..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d cihaz</item>
+ <item quantity="one">%d cihaz</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirimler"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Fener"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobil veri"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> kullanıldı"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Sınır: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> uyarısı"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"İş profili"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Bildirimler ve uygulamalar kapalı"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gece Işığı"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Gün batımı açılacak"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Gün doğumuna kadar"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>, güvenli modda devre dışıdır."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tümünü temizle"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Ekranı bölünmüş olarak kullanmak için burayı sürükleyin"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Uygulamalar arasında geçiş yapmak için yukarı kaydırın"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Yatay Ayırma"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dikey Ayırma"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Özel Ayırma"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Şimdi kapat"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Genişlet"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Daralt"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekran sabitlendi"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Bu işlem, siz sabitlemeyi kaldırana kadar ekranı görünür durumda tutar. Sabitlemeyi kaldırmak için Geri\'ye ve Genel Bakış\'a dokunup basılı tutun."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Bu işlem, siz sabitlemeyi kaldırana kadar ekranı görünür durumda tutar. Sabitlemeyi kaldırmak için Genel bakış\'a dokunup basılı tutun."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Anladım"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Hayır, teşekkürler"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlensin mi?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ayarlardan etkinleştirdiğiniz bir sonraki sefer tekrar görünür."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Gizle"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Güç bildirim kontrolleriyle, bir uygulamanın bildirimleri için 0 ile 5 arasında bir önem düzeyi ayarlayabilirsiniz. \n\n"<b>"5. Düzey"</b>" \n- Bildirim listesinin en üstünde gösterilsin \n- Tam ekran kesintisine izin verilsin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"4. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"3. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n\n"<b>"2. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman belirmesin \n- Hiçbir zaman ses çıkarmasın ve titreştirmesin \n\n"<b>"1. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n- Hiçbir zaman ses çıkarmasın veya titreştirmesin \n- Kilit ekranından ve durum çubuğundan gizlensin \n- Bildirim listesinin en altında gösterilsin \n\n"<b>"0. Düzey"</b>" \n- Uygulamadan gelen tüm bildirimler engellensin"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirimler"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Bu bildirimleri artık görmeyeceksiniz"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Bu bildirimleri genellikle kapatıyorsunuz. \nBildirimler gösterilmeye devam edilsin mi?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Bu bildirimler gösterilmeye devam edilsin mi?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Bildirimleri durdur"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Göstermeye devam et"</string>
diff --git a/packages/SystemUI/res/values-uk/config.xml b/packages/SystemUI/res/values-uk/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-uk/config.xml
+++ b/packages/SystemUI/res/values-uk/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f296143..3834753 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -105,12 +105,9 @@
<string name="camera_label" msgid="7261107956054836961">"відкрити камеру"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Виберіть новий макет завдання"</string>
<string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок відбитка пальця"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Значок додатка"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Область довідкового повідомлення"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабування сумісності."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth під’єднано."</string>
@@ -317,6 +314,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi вимкнено"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi увімкнено"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Немає доступних мереж Wi-Fi"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Трансляція"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Трансляція"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Пристрій без назви"</string>
@@ -333,9 +332,13 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"З’єднання…"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доступу"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Увімкнення…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d пристрій</item>
+ <item quantity="few">%d пристрої</item>
+ <item quantity="many">%d пристроїв</item>
+ <item quantity="other">%d пристрою</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Сповіщення"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Ліхтарик"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобільне передавання даних"</string>
@@ -345,10 +348,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Використовується: <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Обмеження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Застереження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Робочий профіль"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Сповіщення й додатки вимкнено"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Нічний режим"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Вмикається ввечері"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"До сходу сонця"</string>
@@ -366,8 +367,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Додаток <xliff:g id="APP">%s</xliff:g> вимкнено в безпечному режимі."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Очистити все"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Перетягніть сюди, щоб увімкнути режим розділеного екрана"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Проводьте пальцем угору, щоб переходити між додатками"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Розділити горизонтально"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Розділити вертикально"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Розділити (власний варіант)"</string>
@@ -508,11 +508,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Вимкнути"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Розгорнути"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Згорнути"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Увімкніть пристрій виведення"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Екран закріплено"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Ви постійно бачитимете екран, доки не відкріпите його. Щоб відкріпити екран, натисніть і втримуйте кнопки \"Назад\" та \"Огляд\"."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ви постійно бачитимете екран, доки не відкріпите його. Щоб відкріпити екран, натисніть і втримуйте кнопку \"Огляд\"."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Зрозуміло"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Ні, дякую"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сховати <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"З’явиться знову, коли ви ввімкнете його в налаштуваннях."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Сховати"</string>
@@ -592,8 +605,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"За допомогою елементів керування сповіщеннями ви можете налаштувати пріоритет сповіщень додатка – від 0 до 5 рівня. \n\n"<b>"Рівень 5"</b>\n"- Показувати сповіщення вгорі списку \n- Виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 4"</b>\n"- Не виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 3"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n\n"<b>"Рівень 2"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n\n"<b>"Рівень 1"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n- Не показувати на заблокованому екрані та в рядку стану \n- Показувати сповіщення внизу списку \n\n"<b>"Рівень 0"</b>\n"- Блокувати всі сповіщення з додатка"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Сповіщення"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ви більше не бачитимете цих сповіщень"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Ви зазвичай закриваєте ці сповіщення. \nПоказувати їх?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Чи показувати ці сповіщення надалі?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Не показувати сповіщення"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Показувати надалі"</string>
diff --git a/packages/SystemUI/res/values-ur/config.xml b/packages/SystemUI/res/values-ur/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-ur/config.xml
+++ b/packages/SystemUI/res/values-ur/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 8099ef4..9ebdfeb 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"کیمرا کھولیں"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"نئے کام کا لے آؤٹ منتخب کریں"</string>
<string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"فنگر پرنٹ آئیکن"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"ایپلیکیشن کا آئیکن"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"امدادی پیغام کا علاقہ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"مطابقت پذیری زوم بٹن۔"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"چھوٹی سے بڑی اسکرین پر زوم کریں۔"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"بلوٹوتھ مربوط ہے۔"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi آف ہے"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi آن ہے"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"کوئی WI-FI نیٹ ورک دستیاب نہیں"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"کاسٹ کریں"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"کاسٹنگ"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"بغیر نام والا آلہ"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"مربوط ہو رہا ہے…"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ٹیدرنگ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ہاٹ اسپاٹ"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"آن ہو رہا ہے…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d آلات</item>
+ <item quantity="one">%d آلہ</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"اطلاعات"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"فلیش لائٹ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"موبائل ڈیٹا"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> استعمال کردہ"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> حد"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> وارننگ"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"دفتری پروفائل"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"اطلاعات اور ایپس آف ہیں"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"نائٹ لائٹ"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"غروب آفتاب کے وقت آن ہوگی"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"طلوع آفتاب تک"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"محفوظ موڈ میں <xliff:g id="APP">%s</xliff:g> غیر فعال ہوتی ہے۔"</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"سبھی کو صاف کریں"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"اسپلٹ اسکرین استعمال کرنے کیلئے یہاں گھسیٹیں"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ایپس سوئچ کرنے کیلئے اوپر سوائپ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"بلحاظ افقی الگ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"بلحاظ عمودی الگ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"بلحاظ حسب ضرورت الگ کریں"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"ابھی بند کریں"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"پھیلائیں"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"سکیڑیں"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"اسکرین پن کردہ ہے"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"یہ اسے اس وقت تک نظر میں رکھتا ہے جب تک آپ اس سے پن ہٹا نہیں دیتے۔ پن ہٹانے کیلئے پیچھے اور مجموعی جائزہ بٹنز کو ٹچ کریں اور دبائے رکھیں۔"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"یہ اسے اس وقت تک نظر میں رکھتا ہے جب تک آپ اس سے پن ہٹا نہیں دیتے۔ پن ہٹانے کیلئے مجموعی جائزہ بٹن کو ٹچ کریں اور دبائے رکھیں۔"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"سمجھ آ گئی"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"نہیں شکریہ"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> کو چھپائیں؟"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"اگلی بار جب آپ اسے ترتیبات میں آن کریں گے تو یہ ظاہر ہوگی۔"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"چھپائیں"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"پاور اطلاع کنٹرولز کے ساتھ آپ کسی ایپ کی اطلاعات کیلئے 0 سے 5 تک اہمیت کی سطح سیٹ کر سکتے ہیں۔ \n\n"<b>"سطح 5"</b>\n"- اطلاعات کی فہرست کے اوپر دکھائیں \n- پوری اسکرین کی مداخلت کی اجازت دیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 4"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 3"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n\n"<b>"سطح 2"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n- کبھی آواز اور ارتعاش پیدا نہ کرنا \n\n"<b>" سطح 1"</b>\n"- پوری اسکرین کی مداخلت کو روکنا \n- کبھی نہ جھانکنا \n- کبھی بھی آواز یا ارتعاش پیدا نہ کرنا\n- مقفل اسکرین اور اسٹیٹس بار سے چھپانا \n - اطلاع کی فہرست کی نیچے دکھانا \n\n"<b>"سطح 0"</b>\n"- ایپ سے تمام اطلاعات مسدود کریں"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"اطلاعات"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"آپ کو یہ اطلاعات مزید دکھائی نہیں دیں گی"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"آپ عام طور پر ان اطلاعات کو مسترد کرتے ہیں۔ \nان کو دکھاتے رہیں؟"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"یہ اطلاعات دکھانا جاری رکھیں؟"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"اطلاعات روک دیں"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"دکھانا جاری رکھیں"</string>
diff --git a/packages/SystemUI/res/values-uz/config.xml b/packages/SystemUI/res/values-uz/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-uz/config.xml
+++ b/packages/SystemUI/res/values-uz/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 612859b..37d8118 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"kamerani ochish"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Yangi vazifa tartibini tanlash"</string>
<string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmoq izi belgisi"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Ilova ikonkasi"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Yordam xabari"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kattalashtirish tugmasi mosligi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kattaroq ekran uchun kichikroqni kattalashtirish."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ulandi."</string>
@@ -288,7 +285,7 @@
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="7106697106764717416">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Garnitura"</string>
- <string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Matn kiritish"</string>
+ <string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Kirish"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Yorqinlik"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Avtomatik burilish"</string>
<string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"Ekranning avtomatik burilishi"</string>
@@ -313,6 +310,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi o‘chiq"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"Wi-Fi yoqilgan"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Hech qanday Wi-Fi tarmog‘i mavjud emas"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Translatsiya"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Translatsiya qilinmoqda"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nomsiz qurilma"</string>
@@ -329,9 +328,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Ulanmoqda…"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Modem rejimi"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Yoqilmoqda…"</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d ta qurilma</item>
+ <item quantity="one">%d ta qurilma</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirishnomalar"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Fonar"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobil internet"</string>
@@ -341,10 +342,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> ishlatilgan"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Cheklov: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Ogohlantirish: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Ishchi profil"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Bildirishnomalar va ilovalar o‘chiq"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Tungi rejim"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Kunbotarda yoqish"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Quyosh chiqqunicha"</string>
@@ -362,8 +361,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Xavfsiz rejimda <xliff:g id="APP">%s</xliff:g> ilovasi o‘chirib qo‘yildi."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hammasini tozalash"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Ekranni bo‘lish xususiyatidan foydalanish uchun bu yerga torting"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Ilovalarni almashtirish uchun ekranni tepaga suring"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gorizontal yo‘nalishda bo‘lish"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikal yo‘nalishda bo‘lish"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Boshqa usulda bo‘lish"</string>
@@ -504,11 +502,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"O‘chiring"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Yoyish"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Yig‘ish"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"Ekran qadaldi"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Ekran yechilmaguncha u o‘zgarmas holatda qoladi. Uni yechish uchun “Orqaga” va “Umumiy ma’lumot” tugmalarini bosib turing."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ekran yechilmaguncha u o‘zgarmas holatda qoladi. Uni yechish uchun “Umumiy ma’lumot” tugmasini bosib turing."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Yo‘q, kerakmas"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> berkitilsinmi?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Keyingi safar sozlamalardan yoqilgan paydo bo‘ladi."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Berkitish"</string>
@@ -588,8 +600,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Bildirishnomalar uchun kengaytirilgan boshqaruv yordamida ilova bildirishnomalarining muhimlik darajasini (0-5) sozlash mumkin. \n\n"<b>"5-daraja"</b>" \n- Bildirishnomani ro‘yxatning boshida ko‘rsatish \n- To‘liq ekranli bildirishnomalarni ko‘rsatish \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"4-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"3-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n\n"<b>"2-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n\n"<b>"1-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n- Ekran qulfi va holat qatorida ko‘rsatmaslik \n- Bildirishnomani ro‘yxatning oxirida ko‘rsatish \n\n"<b>"0-daraja"</b>" \n- Ilovadan keladigan barcha bildirishnomalarni bloklash"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirishnomalar"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Bu bildirishnomalar endi chiqmaydi"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Odatda bunday bildirishnomalarni yopasiz. \nUlar ochiq tursinmi?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Mazkur bildirishnomalar chiqaversinmi?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Chiqmasin"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Ha"</string>
diff --git a/packages/SystemUI/res/values-vi/config.xml b/packages/SystemUI/res/values-vi/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-vi/config.xml
+++ b/packages/SystemUI/res/values-vi/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/config.xml b/packages/SystemUI/res/values-zh-rCN/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-zh-rCN/config.xml
+++ b/packages/SystemUI/res/values-zh-rCN/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/config.xml b/packages/SystemUI/res/values-zh-rHK/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-zh-rHK/config.xml
+++ b/packages/SystemUI/res/values-zh-rHK/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/config.xml b/packages/SystemUI/res/values-zh-rTW/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-zh-rTW/config.xml
+++ b/packages/SystemUI/res/values-zh-rTW/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index e08bf69..9c425b0 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"開啟攝影機"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"選取新工作版面配置"</string>
<string name="cancel" msgid="6442560571259935130">"取消"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"應用程式圖示"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"說明訊息區域"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"藍牙連線已建立。"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"已開啟 Wi-Fi"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"沒有 Wi-Fi 網路"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"投放"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"投放"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名的裝置"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"連線中..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"無線基地台"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"開啟中..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="other">%d 個裝置</item>
+ <item quantity="one">%d 個裝置</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"手電筒"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"行動數據"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"已使用 <xliff:g id="DATA_USED">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"上限為 <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Work 設定檔"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"已關閉通知和應用程式"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜燈"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"於日落時開啟"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"於日出時關閉"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」在安全模式中為停用狀態。"</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"拖曳到這裡即可使用分割畫面"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"向上滑動即可切換應用程式"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
@@ -502,11 +500,25 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"立即停用"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"展開"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"收合"</string>
+ <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
+ <skip />
<string name="screen_pinning_title" msgid="3273740381976175811">"螢幕已固定"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。按住 [返回] 按鈕和 [總覽] 按鈕即可取消固定。"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。按住 [總覽] 按鈕即可取消固定。"</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"知道了"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"不用了,謝謝"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"隱藏<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"只要在設定頁面中重新啟用,就能再次看到快捷設定選項。"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"隱藏"</string>
@@ -586,8 +598,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"只要使用電源通知控制項,你就能為應用程式通知設定從 0 到 5 的重要性等級。\n\n"<b>"等級 5"</b>" \n- 顯示在通知清單頂端 \n- 允許全螢幕通知 \n- 一律允許短暫顯示通知 \n\n"<b>"等級 4"</b>" \n- 禁止全螢幕通知 \n- 一律允許短暫顯示通知 \n\n"<b>"等級 3"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n\n"<b>"等級 2"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n- 一律不發出音效或震動 \n\n"<b>"等級 1"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n- 一律不發出音效或震動 \n- 在鎖定畫面和狀態列中隱藏 \n- 顯示在通知清單底端 \n\n"<b>"等級 0"</b>" \n- 封鎖應用程式的所有通知"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"你不會再看到這些通知"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"你通常會關閉這些通知。\n要繼續顯示通知嗎?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"要繼續顯示這些通知嗎?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"停止顯示通知"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string>
diff --git a/packages/SystemUI/res/values-zu/config.xml b/packages/SystemUI/res/values-zu/config.xml
index 477f219..5309563 100644
--- a/packages/SystemUI/res/values-zu/config.xml
+++ b/packages/SystemUI/res/values-zu/config.xml
@@ -22,6 +22,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a99b8cc..d291a19 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -103,12 +103,9 @@
<string name="camera_label" msgid="7261107956054836961">"vula ikhamera"</string>
<string name="recents_caption_resize" msgid="3517056471774958200">"Khetha isakhiwo somsebenzi omusha"</string>
<string name="cancel" msgid="6442560571259935130">"Khansela"</string>
- <!-- no translation found for accessibility_fingerprint_dialog_fingerprint_icon (3125122495414253226) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_app_icon (3228052542929174609) -->
- <skip />
- <!-- no translation found for accessibility_fingerprint_dialog_help_area (5730471601819225159) -->
- <skip />
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Isithonjana sezigxivizo zeminwe"</string>
+ <string name="accessibility_fingerprint_dialog_app_icon" msgid="3228052542929174609">"Isithonjana sohlelo lokusebenza"</string>
+ <string name="accessibility_fingerprint_dialog_help_area" msgid="5730471601819225159">"Indawo yosizo lomlayezo"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Inkinobho evumelekile yokusondeza"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Sondeza kancane esikrinini esikhudlwana"</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ixhunyiwe"</string>
@@ -311,6 +308,8 @@
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"I-Wi-Fi icimile"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"I-Wi-Fi ivuliwe"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Awekho amanethiwekhi we-Wi-Fi atholakalayo"</string>
+ <!-- no translation found for quick_settings_alarm_title (2416759007342260676) -->
+ <skip />
<string name="quick_settings_cast_title" msgid="7709016546426454729">"Abalingisi"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"Ukusakaza"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Idivayisi engenalo igama"</string>
@@ -327,9 +326,11 @@
<string name="quick_settings_connecting" msgid="47623027419264404">"Iyaxhuma..."</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ukusebenzisa njengemodemu"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"I-Hotspot"</string>
- <!-- no translation found for quick_settings_hotspot_secondary_label_transient (7161046712706277215) -->
- <skip />
- <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
+ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7161046712706277215">"Iyavula..."</string>
+ <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="2324635800672199428">
+ <item quantity="one">%d amadivayisi</item>
+ <item quantity="other">%d amadivayisi</item>
+ </plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Izaziso"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"I-Flashlight"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Idatha yeselula"</string>
@@ -339,10 +340,8 @@
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> isetshenzisiwe"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"<xliff:g id="DATA_LIMIT">%s</xliff:g> umkhawulo"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string>
- <!-- no translation found for quick_settings_work_mode_on_label (3421274215098764735) -->
- <skip />
- <!-- no translation found for quick_settings_work_mode_off_label (8856918707867192186) -->
- <skip />
+ <string name="quick_settings_work_mode_on_label" msgid="3421274215098764735">"Iphrofayela yomsebenzi"</string>
+ <string name="quick_settings_work_mode_off_label" msgid="8856918707867192186">"Izaziso nezinhlelo zokusebenza kuvaliwe"</string>
<string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ukukhanya kwasebusuku"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Kuvulwe ekushoneni kwelanga"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Kuze kube sekuphumeni kwelanga"</string>
@@ -360,8 +359,7 @@
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"I-<xliff:g id="APP">%s</xliff:g> ikhutshaziwe kumodi yokuphepha."</string>
<string name="recents_stack_action_button_label" msgid="6593727103310426253">"Sula konke"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Hudulela lapha ukuze usebenzise ukuhlukanisa kwesikrini"</string>
- <!-- no translation found for recents_swipe_up_onboarding (3824607135920170001) -->
- <skip />
+ <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swayiphela phezulu ukuze ushintshe izinhlelo zokusebenza"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Hlukanisa okuvundlile"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Hlukanisa okumile"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Hlukanisa kwezifiso"</string>
@@ -502,11 +500,24 @@
<string name="volume_zen_end_now" msgid="6930243045593601084">"Vala manje"</string>
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Nweba"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Goqa"</string>
+ <string name="accessibility_output_chooser" msgid="8185317493017988680">"Shintsha idivayisi yokukhipha"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Isikrini siphiniwe"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Lokhu kuyigcina ibukeka uze ususe ukuphina. Thinta uphinde ubambe okuthi Emuva Nokubuka konke ukuze ususe ukuphina."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
+ <skip />
<string name="screen_pinning_description_accessible" msgid="426190689254018656">"Lokhu kuyigcina ibukeka uze ususe ukuphina. Thinta uphinde ubambe Ukubuka konke ukuze ususe ukuphina."</string>
+ <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
+ <skip />
+ <!-- no translation found for screen_pinning_toast_recents_invisible (8252402309499161281) -->
+ <skip />
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ngiyitholile"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Cha ngiyabonga"</string>
+ <!-- no translation found for screen_pinning_start (1022122128489278317) -->
+ <skip />
+ <!-- no translation found for screen_pinning_exit (5187339744262325372) -->
+ <skip />
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fihla i-<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Izovela ngesikhathi esilandelayo uma uvule lesi silungiselelo."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fihla"</string>
@@ -586,8 +597,7 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"Ngezilawuli zesaziso zamandla, ungasetha ileveli ebalulekile kusuka ku-0 kuya ku-5 kusuka kuzaziso zohlelo lokusebenza. \n\n"<b>"Ileveli 5"</b>" \n- Ibonisa phezulu kuhlu lwesaziso \n- Vumela ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 4"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 3"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n\n"<b>"Ileveli 2"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo nokudlidliza \n\n"<b>"Ileveli 1"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo noma ukudlidliza \n- Fihla kusuka kusikrini sokukhiya nebha yesimo \n- Bonisa phansi kohlu lwesaziso \n\n"<b>"Ileveli 0"</b>" \n- Vimbela zonke izaziso kusuka kuhlelo lokusebenza"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Izaziso"</string>
<string name="notification_channel_disabled" msgid="344536703863700565">"Ngeke usabona lezi zaziso"</string>
- <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
- <skip />
+ <string name="inline_blocking_helper" msgid="3055064577771478591">"Uvamise ukucashisa lezi zaziso. \nQhubeka ulokhu uzibonisa?"</string>
<string name="inline_keep_showing" msgid="8945102997083836858">"Qhubeka nokubonisa lezi zaziso?"</string>
<string name="inline_stop_button" msgid="4172980096860941033">"Misa izaziso"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Qhubeka nokubonisa"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ae910fe..f2e5d3b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -46,7 +46,7 @@
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
<!-- Component name of launcher service for overview to connect to -->
- <string name="config_overviewServiceComponent" translateable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string>
+ <string name="config_overviewServiceComponent" translatable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string>
<!-- Whether or not we show the number in the bar. -->
<bool name="config_statusBarShowNumber">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3db79d7..a9021ae 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -262,7 +262,11 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">120dp</dimen>
+ <!-- the amount the volume panel should be offset at the end from the view next to it (or
+ the scren edge, in portrait-->
+ <dimen name="volume_dialog_base_margin">12dp</dimen>
+
+ <dimen name="volume_dialog_panel_width">100dp</dimen>
<dimen name="output_chooser_panel_width">320dp</dimen>
@@ -332,6 +336,8 @@
<dimen name="qs_footer_padding_start">16dp</dimen>
<dimen name="qs_footer_padding_end">24dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
+ <!-- Difference between drag handle margin in QQS and expanded QS -->
+ <dimen name="qs_footer_drag_handle_offset">6dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
@@ -895,11 +901,33 @@
<dimen name="fingerprint_dialog_fp_icon_size">60dp</dimen>
<dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
- <!-- WirelessCharging Animation values -->
- <!-- Starting text size of batteryLevel for wireless charging animation -->
- <dimen name="config_batteryLevelTextSizeStart" format="float">5.0</dimen>
- <!-- Ending text size of batteryLevel for wireless charging animation -->
- <dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen>
- <!-- Wireless Charging battery level text animation duration -->
- <integer name="config_batteryLevelTextAnimationDuration">400</integer>
+ <!-- Wireless Charging Animation values -->
+ <dimen name="wireless_charging_dots_radius_start">0dp</dimen>
+ <dimen name="wireless_charging_dots_radius_end">4dp</dimen>
+ <dimen name="wireless_charging_circle_radius_start">28dp</dimen>
+ <dimen name="wireless_charging_circle_radius_end">92dp</dimen>
+ <integer name="wireless_charging_angle_offset">20</integer>
+ <integer name="wireless_charging_scale_dots_duration">83</integer>
+ <integer name="wireless_charging_num_dots">20</integer>
+ <!-- Starting text size in dp of batteryLevel for wireless charging animation -->
+ <dimen name="wireless_charging_anim_battery_level_text_size_start">0dp</dimen>
+ <!-- Ending text size in dp of batteryLevel for wireless charging animation -->
+ <dimen name="wireless_charging_anim_battery_level_text_size_end">14dp</dimen>
+ <!-- time until battery info is at full opacity-->
+ <integer name="wireless_charging_anim_opacity_offset">80</integer>
+ <!-- duration batteryLevel opacity goes from 0 to 1 duration -->
+ <integer name="wireless_charging_battery_level_text_opacity_duration">117</integer>
+ <!-- battery text scale animation duration -->
+ <integer name="wireless_charging_battery_level_text_scale_animation_duration">367</integer>
+ <!--time until wireless charging animation starts to fade-->
+ <integer name="wireless_charging_fade_offset">920</integer>
+ <!-- duration wireless charging animation takes to full fade to 0 opacity -->
+ <integer name="wireless_charging_fade_duration">200</integer>
+
+ <!-- Wired charging on AOD, text animation duration -->
+ <integer name="wired_charging_aod_text_animation_duration_down">500</integer>
+ <!-- Wired charging on AOD, text animation duration -->
+ <integer name="wired_charging_aod_text_animation_duration_up">300</integer>
+ <!-- Wired charging on AOD, text animation distance -->
+ <integer name="wired_charging_aod_text_animation_distance">-30</integer>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index f9aa821..2d30f4c 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -47,7 +47,6 @@
<item type="id" name="qs_icon_tag"/>
<item type="id" name="qs_slash_tag"/>
<item type="id" name="scrim"/>
- <item type="id" name="scrim_blanking"/>
<item type="id" name="scrim_target"/>
<item type="id" name="scrim_alpha_start"/>
<item type="id" name="scrim_alpha_end"/>
@@ -58,6 +57,8 @@
<item type="id" name="notification_plugin"/>
<item type="id" name="transformation_start_x_tag"/>
<item type="id" name="transformation_start_y_tag"/>
+ <item type="id" name="transformation_start_actual_width"/>
+ <item type="id" name="transformation_start_actual_height"/>
<item type="id" name="transformation_start_scale_x_tag"/>
<item type="id" name="transformation_start_scale_y_tag"/>
<item type="id" name="continuous_clipping_tag"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fadcbcd..fc5ea458 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -842,8 +842,6 @@
<string name="recents_stack_action_button_label">Clear all</string>
<!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
<string name="recents_drag_hint_message">Drag here to use split screen</string>
- <!-- Recents: Text that shows above the nav bar after launching a few apps. [CHAR LIMIT=NONE] -->
- <string name="recents_swipe_up_onboarding">Swipe up to switch apps</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2497d20..d2ed4d1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -410,16 +410,11 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
- <style name="TextAppearance.Volume.ZenSummary">
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">sans-serif-medium</item>
+ <style name="TextAppearance.Volume.Header.Secondary">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textColor">?android:attr/textColorTertiary</item>
</style>
- <style name="TextAppearance.Volume.ZenDetail">
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- </style>
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
<item name="android:background">@drawable/btn_borderless_rect</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 64fa9c6..cd9e126 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -21,9 +21,40 @@
oneway interface IOverviewProxy {
void onBind(in ISystemUiProxy sysUiProxy);
+
+ /**
+ * Proxies motion events from the nav bar in SystemUI to the OverviewProxyService. The sender
+ * guarantees the following order of events:
+ *
+ * Normal gesture: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, UP
+ * Quick switch: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, SWITCH
+ * Quick scrub: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, SCRUB_START, SCRUB_PROGRESS*, SCRUB_END
+ *
+ * Once quick switch/scrub is sent, then no further motion events will be provided.
+ */
void onMotionEvent(in MotionEvent event);
+
+ /**
+ * Sent when a user has quickly flinged on the nav bar to switch tasks. Once this event is sent
+ * the caller will stop sending any motion events.
+ */
void onQuickSwitch();
+
+ /**
+ * Sent when the user starts to actively scrub the nav bar to switch tasks. Once this event is
+ * sent the caller will stop sending any motion events.
+ */
void onQuickScrubStart();
+
+ /**
+ * Sent when the user stops actively scrubbing the nav bar to switch tasks. Once this event is
+ * sent the caller will stop sending any motion events.
+ */
void onQuickScrubEnd();
+
+ /**
+ * Sent for each movement over the nav bar while the user is scrubbing it to switch tasks. Once
+ * this event is sent the caller will stop sending any motion events.
+ */
void onQuickScrubProgress(float progress);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 98ede4e..4cf817e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -39,4 +39,9 @@
* Called when the overview service has started the recents animation.
*/
void onRecentsAnimationStarted();
+
+ /**
+ * Specifies the text to be shown for onboarding the new swipe-up gesture to access recents.
+ */
+ void setRecentsOnboardingText(CharSequence text);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index f9e1069..62bd72f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -16,6 +16,8 @@
package com.android.systemui.shared.system;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -31,6 +33,7 @@
import android.app.AppGlobals;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration.ActivityType;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -40,6 +43,7 @@
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -47,12 +51,14 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -267,11 +273,20 @@
runner = new IRecentsAnimationRunner.Stub() {
public void onAnimationStart(IRecentsAnimationController controller,
RemoteAnimationTarget[] apps) {
+ final Rect stableInsets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
+ onAnimationStart_New(controller, apps, stableInsets, null);
+ }
+
+ public void onAnimationStart_New(IRecentsAnimationController controller,
+ RemoteAnimationTarget[] apps, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
final RecentsAnimationControllerCompat controllerCompat =
new RecentsAnimationControllerCompat(controller);
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
- animationHandler.onAnimationStart(controllerCompat, appsCompat);
+ animationHandler.onAnimationStart(controllerCompat, appsCompat,
+ homeContentInsets, minimizedHomeBounds);
}
public void onAnimationCanceled() {
@@ -436,4 +451,23 @@
Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
}
}
+
+ /**
+ * @return whether there is currently a locked task (ie. in screen pinning).
+ */
+ public boolean isLockToAppActive() {
+ try {
+ return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @return whether screen pinning is enabled.
+ */
+ public boolean isLockToAppEnabled() {
+ final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver();
+ return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0;
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index bf6179d..a473db1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -16,13 +16,15 @@
package com.android.systemui.shared.system;
+import android.graphics.Rect;
+
public interface RecentsAnimationListener {
/**
* Called when the animation into Recents can start. This call is made on the binder thread.
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps);
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 3871980..b8c5049 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -16,6 +16,9 @@
package com.android.systemui.shared.system;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+
+import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
@@ -37,7 +40,10 @@
public final Point position;
public final Rect sourceContainerBounds;
+ private final RemoteAnimationTarget mTarget;
+
public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
+ mTarget = app;
taskId = app.taskId;
mode = app.mode;
leash = new SurfaceControlCompat(app.leash);
@@ -56,4 +62,18 @@
}
return appsCompat;
}
+
+ /**
+ * TODO: Get as a method for compatibility (will move into ctor once Launcher updates)
+ */
+ public Rect getContentInsets() {
+ return mTarget.contentInsets;
+ }
+
+ /**
+ * TODO: Get as a method for compatibility (will move into ctor once Launcher updates)
+ */
+ public boolean isAssistantActivityType() {
+ return mTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_ASSISTANT;
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/tests/Android.mk b/packages/SystemUI/shared/tests/Android.mk
index 239a4e3..1715983 100644
--- a/packages/SystemUI/shared/tests/Android.mk
+++ b/packages/SystemUI/shared/tests/Android.mk
@@ -41,7 +41,7 @@
testables \
truth-prebuilt \
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
# sign this with platform cert, so this test is allowed to inject key events into
# UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index b8cfa3e..aeef496 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -18,6 +18,7 @@
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
@@ -43,6 +44,7 @@
public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
public static final Interpolator PANEL_CLOSE_ACCELERATED
= new PathInterpolator(0.3f, 0, 0.5f, 1);
+ public static final Interpolator BOUNCE = new BounceInterpolator();
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 8d8b726..b30b0c5 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -66,6 +66,7 @@
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
+ private CharSequence mOnboardingText;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -105,6 +106,10 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ public void setRecentsOnboardingText(CharSequence text) {
+ mOnboardingText = text;
+ }
};
private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
@@ -199,7 +204,7 @@
launcherServiceIntent.setComponent(mLauncherComponentName);
boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
- UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
+ UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
if (!bound) {
// Retry after exponential backoff timeout
final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
@@ -223,8 +228,8 @@
return mOverviewProxy;
}
- public ComponentName getLauncherComponent() {
- return mLauncherComponentName;
+ public CharSequence getOnboardingText() {
+ return mOnboardingText;
}
private void disconnectFromLauncherService() {
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 9319bc6..adb4e33 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -49,7 +49,7 @@
Key.QS_NIGHTDISPLAY_ADDED,
Key.SEEN_MULTI_USER,
Key.NUM_APPS_LAUNCHED,
- Key.HAS_SWIPED_UP_FOR_RECENTS,
+ Key.HAS_SEEN_RECENTS_ONBOARDING,
})
public @interface Key {
@Deprecated
@@ -78,7 +78,7 @@
String QS_NIGHTDISPLAY_ADDED = "QsNightDisplayAdded";
String SEEN_MULTI_USER = "HasSeenMultiUser";
String NUM_APPS_LAUNCHED = "NumAppsLaunched";
- String HAS_SWIPED_UP_FOR_RECENTS = "HasSwipedUpForRecents";
+ String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 348855b..afc9629 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
@@ -36,13 +35,18 @@
*/
public class WirelessChargingAnimation {
- public static final long DURATION = 1400;
+ public static final long DURATION = 1133;
private static final String TAG = "WirelessChargingView";
private static final boolean LOCAL_LOGV = false;
private final WirelessChargingView mCurrentWirelessChargingView;
private static WirelessChargingView mPreviousWirelessChargingView;
+ public interface Callback {
+ void onAnimationStarting();
+ void onAnimationEnded();
+ }
+
/**
* Constructs an empty WirelessChargingAnimation object. If looper is null,
* Looper.myLooper() is used. Must set
@@ -51,9 +55,9 @@
* @hide
*/
public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
- batteryLevel) {
+ batteryLevel, Callback callback) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
- batteryLevel);
+ batteryLevel, callback);
}
/**
@@ -61,8 +65,8 @@
* @hide
*/
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
- @Nullable Looper looper, int batteryLevel) {
- return new WirelessChargingAnimation(context, looper, batteryLevel);
+ @Nullable Looper looper, int batteryLevel, Callback callback) {
+ return new WirelessChargingAnimation(context, looper, batteryLevel, callback);
}
/**
@@ -95,8 +99,11 @@
private View mView;
private View mNextView;
private WindowManager mWM;
+ private Callback mCallback;
- public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel) {
+ public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel,
+ Callback callback) {
+ mCallback = callback;
mNextView = new WirelessChargingLayout(context, batteryLevel);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
@@ -149,6 +156,8 @@
}
public void hide(long duration) {
+ mHandler.removeMessages(HIDE);
+
if (LOCAL_LOGV) Log.v(TAG, "HIDE: " + this);
mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
}
@@ -169,18 +178,6 @@
context = mView.getContext();
}
mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- // We can resolve the Gravity here by using the Locale for getting
- // the layout direction
- final Configuration config = mView.getContext().getResources().getConfiguration();
- final int gravity = Gravity.getAbsoluteGravity(mGravity,
- config.getLayoutDirection());
- mParams.gravity = gravity;
- if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
- mParams.horizontalWeight = 1.0f;
- }
- if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
- mParams.verticalWeight = 1.0f;
- }
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = DURATION;
@@ -191,6 +188,9 @@
if (LOCAL_LOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
try {
+ if (mCallback != null) {
+ mCallback.onAnimationStarting();
+ }
mWM.addView(mView, mParams);
} catch (WindowManager.BadTokenException e) {
Slog.d(TAG, "Unable to add wireless charging view. " + e);
@@ -203,6 +203,9 @@
if (mView != null) {
if (mView.getParent() != null) {
if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
+ if (mCallback != null) {
+ mCallback.onAnimationEnded();
+ }
mWM.removeViewImmediate(mView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index c78ea56..8f87d64 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -16,13 +16,16 @@
package com.android.systemui.charging;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import java.text.NumberFormat;
@@ -52,10 +55,9 @@
init(c, attrs, -1);
}
- private void init(Context c, AttributeSet attrs, int batteryLevel) {
+ private void init(Context context, AttributeSet attrs, int batteryLevel) {
final int mBatteryLevel = batteryLevel;
-
- inflate(c, R.layout.wireless_charging_layout, this);
+ inflate(context, R.layout.wireless_charging_layout, this);
// where the circle animation occurs:
final WirelessChargingView mChargingView = findViewById(R.id.wireless_charging_view);
@@ -68,14 +70,57 @@
if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
-
- ValueAnimator animator = ObjectAnimator.ofFloat(mPercentage, "textSize",
- getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeStart),
- getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeEnd));
-
- animator.setDuration((long) getContext().getResources().getInteger(
- R.integer.config_batteryLevelTextAnimationDuration));
- animator.start();
+ mPercentage.setAlpha(0);
}
+
+ final long chargingAnimationFadeStartOffset = (long) context.getResources().getInteger(
+ R.integer.wireless_charging_fade_offset);
+ final long chargingAnimationFadeDuration = (long) context.getResources().getInteger(
+ R.integer.wireless_charging_fade_duration);
+ final int batteryLevelTextSizeStart = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_anim_battery_level_text_size_start);
+ final int batteryLevelTextSizeEnd = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_anim_battery_level_text_size_end);
+
+ // Animation Scale: battery percentage text scales from 0% to 100%
+ ValueAnimator textSizeAnimator = ObjectAnimator.ofFloat(mPercentage, "textSize",
+ batteryLevelTextSizeStart, batteryLevelTextSizeEnd);
+ textSizeAnimator.setInterpolator(new PathInterpolator(0, 0, 0, 1));
+ textSizeAnimator.setDuration((long) context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_scale_animation_duration));
+
+ // Animation Opacity: battery percentage text transitions from 0 to 1 opacity
+ ValueAnimator textOpacityAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 0, 1);
+ textOpacityAnimator.setInterpolator(Interpolators.LINEAR);
+ textOpacityAnimator.setDuration((long) context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_opacity_duration));
+ textOpacityAnimator.setStartDelay((long) context.getResources().getInteger(
+ R.integer.wireless_charging_anim_opacity_offset));
+
+ // Animation Opacity: battery percentage text fades from 1 to 0 opacity
+ ValueAnimator textFadeAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 1, 0);
+ textFadeAnimator.setDuration(chargingAnimationFadeDuration);
+ textFadeAnimator.setInterpolator(Interpolators.LINEAR);
+ textFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // Animation Opacity: wireless charging circle animation fades from 1 to 0 opacity
+ ValueAnimator circleFadeAnimator = ObjectAnimator.ofFloat(mChargingView, "alpha",
+ 1, 0);
+ circleFadeAnimator.setDuration(chargingAnimationFadeDuration);
+ circleFadeAnimator.setInterpolator(Interpolators.LINEAR);
+ circleFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // Animation Opacity: secondary text animation fades from 1 to 0 opacity
+ ValueAnimator secondaryTextFadeAnimator = ObjectAnimator.ofFloat(mSecondaryText, "alpha",
+ 1, 0);
+ circleFadeAnimator.setDuration(chargingAnimationFadeDuration);
+ secondaryTextFadeAnimator.setInterpolator(Interpolators.LINEAR);
+ secondaryTextFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // play all animations together
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator,
+ circleFadeAnimator, secondaryTextFadeAnimator);
+ animatorSet.start();
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
index f5edf52..19c6dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
@@ -21,10 +21,10 @@
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
final class WirelessChargingView extends View {
@@ -33,21 +33,21 @@
private float mPathGone;
private float mInterpolatedPathGone;
private long mAnimationStartTime;
- private long mStartSpinCircleAnimationTime;
- private long mAnimationOffset = 500;
- private long mTotalAnimationDuration = WirelessChargingAnimation.DURATION - mAnimationOffset;
- private long mExpandingCircle = (long) (mTotalAnimationDuration * .9);
- private long mSpinCircleAnimationTime = mTotalAnimationDuration - mExpandingCircle;
+ private long mScaleDotsDuration;
- private boolean mFinishedAnimatingSpinningCircles = false;
+ private boolean mFinishedAnimatingDots = false;
+ private int mNumDots;
- private int mStartAngle = -90;
- private int mNumSmallCircles = 20;
- private int mSmallCircleRadius = 10;
+ private double mAngleOffset;
+ private double mCurrAngleOffset;
- private int mMainCircleStartRadius = 100;
- private int mMainCircleEndRadius = 230;
- private int mMainCircleCurrentRadius = mMainCircleStartRadius;
+ private int mDotsRadiusStart;
+ private int mDotsRadiusEnd;
+ private int mCurrDotRadius;
+
+ private int mMainCircleStartRadius;
+ private int mMainCircleEndRadius;
+ private int mMainCircleCurrentRadius;
private int mCenterX;
private int mCenterY;
@@ -72,8 +72,25 @@
public void init(Context context, AttributeSet attr) {
mContext = context;
+
+ mDotsRadiusStart = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_dots_radius_start);
+ mDotsRadiusEnd = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_dots_radius_end);
+
+ mMainCircleStartRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_circle_radius_start);
+ mMainCircleEndRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_circle_radius_end);
+ mMainCircleCurrentRadius = mMainCircleStartRadius;
+
+ mAngleOffset = context.getResources().getInteger(R.integer.wireless_charging_angle_offset);
+ mScaleDotsDuration = (long) context.getResources().getInteger(
+ R.integer.wireless_charging_scale_dots_duration);
+ mNumDots = context.getResources().getInteger(R.integer.wireless_charging_num_dots);
+
setupPaint();
- mInterpolator = new DecelerateInterpolator();
+ mInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
}
private void setupPaint() {
@@ -92,64 +109,62 @@
updateDrawingParameters();
drawCircles(canvas);
- if (!mFinishedAnimatingSpinningCircles) {
+ if (!mFinishedAnimatingDots) {
invalidate();
}
}
/**
* Draws a larger circle of radius {@link WirelessChargingView#mMainCircleEndRadius} composed of
- * {@link WirelessChargingView#mNumSmallCircles} smaller circles
+ * {@link WirelessChargingView#mNumDots} smaller circles
* @param canvas
*/
private void drawCircles(Canvas canvas) {
mCenterX = canvas.getWidth() / 2;
mCenterY = canvas.getHeight() / 2;
- // angleOffset makes small circles look like they're moving around the main circle
- float angleOffset = mPathGone * 10;
-
- // draws mNumSmallCircles to compose a larger, main circle
- for (int circle = 0; circle < mNumSmallCircles; circle++) {
- double angle = ((mStartAngle + angleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI)
- / mNumSmallCircles));
+ // draws mNumDots to compose a larger, main circle
+ for (int circle = 0; circle < mNumDots; circle++) {
+ double angle = ((mCurrAngleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI)
+ / mNumDots));
int x = (int) (mCenterX + Math.cos(angle) * (mMainCircleCurrentRadius +
- mSmallCircleRadius));
+ mCurrDotRadius));
int y = (int) (mCenterY + Math.sin(angle) * (mMainCircleCurrentRadius +
- mSmallCircleRadius));
+ mCurrDotRadius));
- canvas.drawCircle(x, y, mSmallCircleRadius, mPaint);
+ canvas.drawCircle(x, y, mCurrDotRadius, mPaint);
}
- if (mMainCircleCurrentRadius >= mMainCircleEndRadius && !isSpinCircleAnimationStarted()) {
- mStartSpinCircleAnimationTime = System.currentTimeMillis();
+ if (mMainCircleCurrentRadius >= mMainCircleEndRadius) {
+ mFinishedAnimatingDots = true;
}
-
- if (isSpinAnimationFinished()) {
- mFinishedAnimatingSpinningCircles = true;
- }
- }
-
- private boolean isSpinCircleAnimationStarted() {
- return mStartSpinCircleAnimationTime != 0;
- }
-
- private boolean isSpinAnimationFinished() {
- return isSpinCircleAnimationStarted() && System.currentTimeMillis() -
- mStartSpinCircleAnimationTime > mSpinCircleAnimationTime;
}
private void updateDrawingParameters() {
- mPathGone = getPathGone(System.currentTimeMillis());
+ long now = System.currentTimeMillis();
+ long timeSinceStart = now - mAnimationStartTime;
+ mPathGone = getPathGone(now);
mInterpolatedPathGone = mInterpolator.getInterpolation(mPathGone);
+ // Position Dots: update main circle radius (that the dots compose)
if (mPathGone < 1.0f) {
mMainCircleCurrentRadius = mMainCircleStartRadius + (int) (mInterpolatedPathGone *
(mMainCircleEndRadius - mMainCircleStartRadius));
} else {
mMainCircleCurrentRadius = mMainCircleEndRadius;
}
+
+ // Scale Dots: update dot radius
+ if (timeSinceStart < mScaleDotsDuration) {
+ mCurrDotRadius = mDotsRadiusStart + (int) (mInterpolator.getInterpolation((float)
+ timeSinceStart / mScaleDotsDuration) * (mDotsRadiusEnd - mDotsRadiusStart));
+ } else {
+ mCurrDotRadius = mDotsRadiusEnd;
+ }
+
+ // Rotation Dot Group: dots rotate from 0 to 20 degrees
+ mCurrAngleOffset = mAngleOffset * mPathGone;
}
/**
@@ -158,6 +173,6 @@
* For values > 1.0 the larger circle has been drawn and further animation can occur
*/
private float getPathGone(long now) {
- return (float) (now - mAnimationStartTime) / (mExpandingCircle);
+ return (float) (now - mAnimationStartTime) / (WirelessChargingAnimation.DURATION);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 653e500..eedc50f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -707,6 +707,9 @@
&& !mLockPatternUtils.isLockScreenDisabled(
KeyguardUpdateMonitor.getCurrentUser()),
mSecondaryDisplayShowing, true /* forceCallbacks */);
+ } else {
+ // The system's keyguard is disabled or missing.
+ setShowingLocked(false, mSecondaryDisplayShowing, true);
}
mStatusBarKeyguardViewManager =
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index aa56694..3a2b12f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -26,10 +26,6 @@
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.icu.text.MeasureFormat;
-import android.icu.text.MeasureFormat.FormatWidth;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
import android.media.AudioAttributes;
import android.os.AsyncTask;
import android.os.Handler;
@@ -37,11 +33,11 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.PowerUtil;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -49,8 +45,6 @@
import java.io.PrintWriter;
import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
@@ -200,12 +194,7 @@
// override notification copy if hybrid notification enabled
if (mEstimate != null) {
title = mContext.getString(R.string.battery_low_title_hybrid);
- contentText = mContext.getString(
- mEstimate.isBasedOnUsage
- ? R.string.battery_low_percent_format_hybrid
- : R.string.battery_low_percent_format_hybrid_short,
- percentage,
- getTimeRemainingFormatted());
+ contentText = getHybridContentString(percentage);
}
final Notification.Builder nb =
@@ -239,21 +228,12 @@
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
- @VisibleForTesting
- String getTimeRemainingFormatted() {
- final Locale currentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
- MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.NARROW);
-
- final long remainder = mEstimate.estimateMillis % DateUtils.HOUR_IN_MILLIS;
- final long hours = TimeUnit.MILLISECONDS.toHours(
- mEstimate.estimateMillis - remainder);
- // round down to the nearest 15 min for now to not appear overly precise
- final long minutes = TimeUnit.MILLISECONDS.toMinutes(
- remainder - (remainder % TimeUnit.MINUTES.toMillis(15)));
- final Measure hoursMeasure = new Measure(hours, MeasureUnit.HOUR);
- final Measure minutesMeasure = new Measure(minutes, MeasureUnit.MINUTE);
-
- return frmt.formatMeasures(hoursMeasure, minutesMeasure);
+ private String getHybridContentString(String percentage) {
+ return PowerUtil.getBatteryRemainingStringFormatted(
+ mContext,
+ mEstimate.estimateMillis,
+ percentage,
+ mEstimate.isBasedOnUsage);
}
private PendingIntent pendingBroadcast(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index e661fa7..ea2a432 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -142,8 +142,12 @@
final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
- // NOTE: Keep the logic in sync with BatteryService.
- // TODO: Propagate this value from BatteryService to system UI, really.
+ // Note LOW_POWER_MODE_TRIGGER_LEVEL can take any value between 0 and 100, but
+ // for the UI purposes, let's cap it at 15% -- i.e. even if the trigger level is higher
+ // like 50%, let's not show the "low battery" notification until it hits
+ // config_lowBatteryWarningLevel, which is 15% by default.
+ // LOW_POWER_MODE_TRIGGER_LEVEL is still used in other places as-is. For example, if it's
+ // 50, then battery saver kicks in when the battery level hits 50%.
int warnLevel = Math.min(defWarnLevel, lowPowerModeTriggerLevel);
if (warnLevel == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 001b409..222c6e82 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -20,11 +20,12 @@
import android.view.View.OnLayoutChangeListener;
import com.android.systemui.Dependency;
-import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.PagedTileLayout.PageListener;
-import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSHost.Callback;
+import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.tuner.TunerService;
@@ -61,6 +62,7 @@
private TouchAnimator mTranslationXAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
+ private TouchAnimator mNonfirstPageDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
private boolean mOnKeyguard;
@@ -83,10 +85,10 @@
QSTileLayout tileLayout = mQsPanel.getTileLayout();
if (tileLayout instanceof PagedTileLayout) {
mPagedLayout = ((PagedTileLayout) tileLayout);
- mPagedLayout.setPageListener(this);
} else {
Log.w(TAG, "QS Not using page layout");
}
+ panel.setPageListener(this);
}
public void onRtlChanged() {
@@ -271,6 +273,9 @@
.setListener(mNonFirstPageListener)
.setEndDelay(.5f)
.build();
+ mNonfirstPageDelayedAnimator = new TouchAnimator.Builder()
+ .setStartDelay(.14f)
+ .addFloat(tileLayout, "alpha", 0, 1).build();
}
private boolean isIconInAnimatedRow(int count) {
@@ -315,6 +320,7 @@
}
} else {
mNonfirstPageAnimator.setPosition(position);
+ mNonfirstPageDelayedAnimator.setPosition(position);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b0d592..6ccb817 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -36,7 +36,6 @@
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
- private final Path mClipPath = new Path();
private int mHeightOverride = -1;
protected View mQSPanel;
@@ -46,7 +45,6 @@
private QSCustomizer mQSCustomizer;
private View mQSFooter;
private View mBackground;
- private float mRadius;
private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -62,8 +60,6 @@
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mBackground = findViewById(R.id.quick_settings_background);
- mRadius = getResources().getDimensionPixelSize(
- Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
@@ -115,18 +111,6 @@
updateExpansion();
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- boolean ret;
- canvas.save();
- if (child != mQSCustomizer) {
- canvas.clipPath(mClipPath);
- }
- ret = super.drawChild(canvas, child, drawingTime);
- canvas.restore();
- return ret;
- }
-
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -146,10 +130,6 @@
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
mBackground.setTop(mQSPanel.getTop());
mBackground.setBottom(height);
-
- ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
- mRadius,
- mClipPath);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 76baee4..fe3ffb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
@@ -47,7 +46,6 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.ExpandableIndicator;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -61,6 +59,7 @@
public class QSFooterImpl extends FrameLayout implements QSFooter,
OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback, CommandQueue.Callbacks {
+
private ActivityStarter mActivityStarter;
private UserInfoController mUserInfoController;
private SettingsButton mSettingsButton;
@@ -75,26 +74,34 @@
private boolean mListening;
private boolean mShowEmergencyCallsOnly;
+ private View mDivider;
protected MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
- protected TouchAnimator mSettingsAlpha;
+ protected TouchAnimator mFooterAnimator;
private float mExpansionAmount;
protected View mEdit;
- private TouchAnimator mAnimator;
+ private TouchAnimator mSettingsCogAnimator;
private View mActionsContainer;
+ private View mDragHandle;
+ private final int mDragHandleExpandOffset;
+ private View mBackground;
public QSFooterImpl(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mDragHandleExpandOffset = getResources().
+ getDimensionPixelSize(R.dimen.qs_footer_drag_handle_offset);
+
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- Resources res = getResources();
-
+ mBackground = findViewById(R.id.qs_footer_background);
+ mDivider = findViewById(R.id.qs_footer_divider);
mEdit = findViewById(android.R.id.edit);
mEdit.setOnClickListener(view ->
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
@@ -109,6 +116,7 @@
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
+ mDragHandle = findViewById(R.id.qs_drag_handle_view);
mActionsContainer = findViewById(R.id.qs_footer_actions_container);
// RenderThread is doing more harm than good when touching the header (to expand quick
@@ -130,7 +138,7 @@
int remaining = (width - numTiles * size) / (numTiles - 1);
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
- mAnimator = new Builder()
+ mSettingsCogAnimator = new Builder()
.addFloat(mSettingsContainer, "translationX",
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
.addFloat(mSettingsButton, "rotation", -120, 0)
@@ -152,18 +160,21 @@
}
private void updateResources() {
- updateSettingsAnimator();
+ updateFooterAnimator();
}
- private void updateSettingsAnimator() {
- mSettingsAlpha = createSettingsAlphaAnimator();
+ private void updateFooterAnimator() {
+ mFooterAnimator = createFooterAnimator();
}
@Nullable
- private TouchAnimator createSettingsAlphaAnimator() {
+ private TouchAnimator createFooterAnimator() {
return new TouchAnimator.Builder()
+ .addFloat(mBackground, "alpha", 0, 0.90f)
+ .addFloat(mDivider, "alpha", 0, 1)
.addFloat(mCarrierText, "alpha", 0, 1)
.addFloat(mActionsContainer, "alpha", 0, 1)
+ .addFloat(mDragHandle, "translationY", 0, -mDragHandleExpandOffset)
.build();
}
@@ -182,10 +193,10 @@
@Override
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
- if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
+ if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
- if (mSettingsAlpha != null) {
- mSettingsAlpha.setPosition(headerExpansionFraction);
+ if (mFooterAnimator != null) {
+ mFooterAnimator.setPosition(headerExpansionFraction);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 00b6c1e..f7c388d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -26,6 +26,7 @@
import android.os.Handler;
import android.os.Message;
import android.service.quicksettings.Tile;
+import android.support.v4.widget.Space;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -97,7 +98,10 @@
R.layout.quick_settings_brightness_dialog, this, false);
mTileLayout = new TileLayout(mContext);
mTileLayout.setListening(mListening);
- mScrollLayout = new QSScrollLayout(mContext, mBrightnessView, (View) mTileLayout);
+ Space space = new Space(mContext);
+ space.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ mContext.getResources().getDimensionPixelSize(R.dimen.qs_footer_height)));
+ mScrollLayout = new QSScrollLayout(mContext, mBrightnessView, (View) mTileLayout, space);
addView(mScrollLayout);
addDivider();
@@ -281,6 +285,20 @@
}
}
+ public void setPageListener(final PagedTileLayout.PageListener pageListener) {
+ if (mTileLayout instanceof PagedTileLayout) {
+ ((PagedTileLayout) mTileLayout).setPageListener(pageListener);
+ } else {
+ mScrollLayout.setOnScrollChangeListener(new OnScrollChangeListener() {
+ @Override
+ public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX,
+ int oldScrollY) {
+ pageListener.onPageChanged(scrollY == 0);
+ }
+ });
+ }
+ }
+
public boolean isExpanded() {
return mExpanded;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
index 9a74787..7b1509d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
@@ -14,15 +14,21 @@
package com.android.systemui.qs;
+import android.animation.ObjectAnimator;
import android.content.Context;
-import android.graphics.Rect;
+import android.graphics.Canvas;
import android.support.v4.widget.NestedScrollView;
+import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.widget.LinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.qs.touch.OverScroll;
+import com.android.systemui.qs.touch.SwipeDetector;
+
/**
* Quick setting scroll view containing the brightness slider and the QS tiles.
*
@@ -32,12 +38,16 @@
*/
public class QSScrollLayout extends NestedScrollView {
private final int mTouchSlop;
+ private final int mFooterHeight;
private int mLastMotionY;
- private Rect mHitRect = new Rect();
+ private final SwipeDetector mSwipeDetector;
+ private final OverScrollHelper mOverScrollHelper;
+ private float mContentTranslationY;
public QSScrollLayout(Context context, View... children) {
super(context);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ mFooterHeight = getResources().getDimensionPixelSize(R.dimen.qs_footer_height);
LinearLayout linearLayout = new LinearLayout(mContext);
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
@@ -47,12 +57,40 @@
linearLayout.addView(view);
}
addView(linearLayout);
+ setOverScrollMode(OVER_SCROLL_NEVER);
+ mOverScrollHelper = new OverScrollHelper();
+ mSwipeDetector = new SwipeDetector(context, mOverScrollHelper, SwipeDetector.VERTICAL);
+ mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!canScrollVertically(1) && !canScrollVertically(-1)) {
+ return false;
+ }
+ mSwipeDetector.onTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev) || mOverScrollHelper.isInOverScroll();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!canScrollVertically(1) && !canScrollVertically(-1)) {
+ return false;
+ }
+ mSwipeDetector.onTouchEvent(ev);
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.translate(0, mContentTranslationY);
+ super.dispatchDraw(canvas);
+ canvas.translate(0, -mContentTranslationY);
}
public boolean shouldIntercept(MotionEvent ev) {
- getHitRect(mHitRect);
- if (!mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
- // Do not intercept touches that are not within this view's bounds.
+ if (ev.getY() > (getBottom() - mFooterHeight)) {
+ // Do not intercept touches that are below the divider between QS and the footer.
return false;
}
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
@@ -80,4 +118,81 @@
parent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
+
+ private void setContentTranslationY(float contentTranslationY) {
+ mContentTranslationY = contentTranslationY;
+ invalidate();
+ }
+
+ private static final Property<QSScrollLayout, Float> CONTENT_TRANS_Y =
+ new Property<QSScrollLayout, Float>(Float.class, "qsScrollLayoutContentTransY") {
+ @Override
+ public Float get(QSScrollLayout qsScrollLayout) {
+ return qsScrollLayout.mContentTranslationY;
+ }
+
+ @Override
+ public void set(QSScrollLayout qsScrollLayout, Float y) {
+ qsScrollLayout.setContentTranslationY(y);
+ }
+ };
+
+ private class OverScrollHelper implements SwipeDetector.Listener {
+ private boolean mIsInOverScroll;
+
+ // We use this value to calculate the actual amount the user has overscrolled.
+ private float mFirstDisplacement = 0;
+
+ @Override
+ public void onDragStart(boolean start) {}
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ // Only overscroll if the user is scrolling down when they're already at the bottom
+ // or scrolling up when they're already at the top.
+ boolean wasInOverScroll = mIsInOverScroll;
+ mIsInOverScroll = (!canScrollVertically(1) && displacement < 0) ||
+ (!canScrollVertically(-1) && displacement > 0);
+
+ if (wasInOverScroll && !mIsInOverScroll) {
+ // Exit overscroll. This can happen when the user is in overscroll and then
+ // scrolls the opposite way. Note that this causes the reset translation animation
+ // to run while the user is dragging, which feels a bit unnatural.
+ reset();
+ } else if (mIsInOverScroll) {
+ if (Float.compare(mFirstDisplacement, 0) == 0) {
+ // Because users can scroll before entering overscroll, we need to
+ // subtract the amount where the user was not in overscroll.
+ mFirstDisplacement = displacement;
+ }
+ float overscrollY = displacement - mFirstDisplacement;
+ setContentTranslationY(getDampedOverScroll(overscrollY));
+ }
+
+ return mIsInOverScroll;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ reset();
+ }
+
+ private void reset() {
+ if (Float.compare(mContentTranslationY, 0) != 0) {
+ ObjectAnimator.ofFloat(QSScrollLayout.this, CONTENT_TRANS_Y, 0)
+ .setDuration(100)
+ .start();
+ }
+ mIsInOverScroll = false;
+ mFirstDisplacement = 0;
+ }
+
+ public boolean isInOverScroll() {
+ return mIsInOverScroll;
+ }
+
+ private float getDampedOverScroll(float y) {
+ return OverScroll.dampedScroll(y, getHeight());
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 3b9e7bc..65135ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -104,6 +104,11 @@
setMeasuredDimension(width, height);
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
private static int exactly(int size) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index aaf6ef5..6205e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -19,8 +19,6 @@
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
-import android.app.AlarmManager;
-import android.app.AlarmManager.AlarmClockInfo;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -30,11 +28,9 @@
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.net.Uri;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.service.notification.ScheduleCalendar;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.quicksettings.Tile;
@@ -56,7 +52,6 @@
import com.android.systemui.SysUIToast;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -197,7 +192,8 @@
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.slash.isSlashed = !state.value;
state.label = getTileLabel();
- state.secondaryLabel = getSecondaryLabel(zen != Global.ZEN_MODE_OFF);
+ state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF,
+ mController.getConfig());
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
switch (zen) {
@@ -226,102 +222,6 @@
state.expandedAccessibilityClassName = Switch.class.getName();
}
- /**
- * Returns the secondary label to use for the given instance of do not disturb.
- * - If turned on manually and end time is known, returns end time.
- * - If turned on by an automatic rule, returns the automatic rule name.
- * - If on due to an app, returns the app name.
- * - If there's a combination of rules/apps that trigger, then shows the one that will
- * last the longest if applicable.
- * @return null if do not disturb is off.
- */
- private String getSecondaryLabel(boolean zenOn) {
- if (!zenOn) {
- return null;
- }
-
- ZenModeConfig config = mController.getConfig();
- String secondaryText = "";
- long latestEndTime = -1;
-
- // DND turned on by manual rule
- if (config.manualRule != null) {
- final Uri id = config.manualRule.conditionId;
- if (config.manualRule.enabler != null) {
- // app triggered manual rule
- String appName = ZenModeConfig.getOwnerCaption(mContext, config.manualRule.enabler);
- if (!appName.isEmpty()) {
- secondaryText = appName;
- }
- } else {
- if (id == null) {
- // Do not disturb manually triggered to remain on forever until turned off
- // No subtext
- return null;
- } else {
- latestEndTime = ZenModeConfig.tryParseCountdownConditionId(id);
- if (latestEndTime > 0) {
- final CharSequence formattedTime = ZenModeConfig.getFormattedTime(mContext,
- latestEndTime, ZenModeConfig.isToday(latestEndTime),
- mContext.getUserId());
- secondaryText = mContext.getString(R.string.qs_dnd_until, formattedTime);
- }
- }
- }
- }
-
- // DND turned on by an automatic rule
- for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (ZenModeConfig.isValidEventConditionId(automaticRule.conditionId) ||
- ZenModeConfig.isValidScheduleConditionId(automaticRule.conditionId)) {
- // set text if automatic rule end time is the latest active rule end time
- long endTime = parseAutomaticRuleEndTime(automaticRule.conditionId);
- if (endTime > latestEndTime) {
- latestEndTime = endTime;
- secondaryText = automaticRule.name;
- }
- } else {
- // set text if 3rd party rule
- return automaticRule.name;
- }
- }
- }
-
- return !secondaryText.equals("") ? secondaryText : null;
- }
-
- private long parseAutomaticRuleEndTime(Uri id) {
- if (ZenModeConfig.isValidEventConditionId(id)) {
- // cannot look up end times for events
- return Long.MAX_VALUE;
- }
-
- if (ZenModeConfig.isValidScheduleConditionId(id)) {
- ScheduleCalendar schedule = ZenModeConfig.toScheduleCalendar(id);
- long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
-
- // check if automatic rule will end on next alarm
- if (schedule.exitAtAlarm()) {
- long nextAlarm = getNextAlarm(mContext);
- schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
- if (schedule.shouldExitForAlarm(endTimeMs)) {
- return nextAlarm;
- }
- }
-
- return endTimeMs;
- }
-
- return -1;
- }
-
- private long getNextAlarm(Context context) {
- final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- final AlarmClockInfo info = alarms.getNextAlarmClock(mContext.getUserId());
- return info != null ? info.getTriggerTime() : 0;
- }
-
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_DND;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 8bdbf28..d7f2a26 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -102,7 +102,7 @@
state.value = locationEnabled;
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION);
if (state.disabledByPolicy == false) {
- checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION_MODE);
+ checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION);
}
state.icon = mIcon;
state.slash.isSlashed = !state.value;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index ea6e174..3597929 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.provider.Settings;
import android.service.quicksettings.Tile;
+import android.support.annotation.StringRes;
import android.widget.Switch;
import com.android.internal.app.ColorDisplayController;
@@ -30,6 +31,7 @@
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class NightDisplayTile extends QSTileImpl<BooleanState>
@@ -39,7 +41,9 @@
* Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the
* nearest hour and add on the AM/PM indicator.
*/
- private static final String APPROXIMATE_HOUR_DATE_TIME_PATTERN = "h a";
+ private static final String HOUR_MINUTE_DATE_TIME_PATTERN = "h a";
+ private static final String APPROXIMATE_HOUR_DATE_TIME_PATTERN = "h:m a";
+
private ColorDisplayController mController;
private boolean mIsListening;
@@ -110,17 +114,26 @@
case ColorDisplayController.AUTO_MODE_CUSTOM:
// User-specified time, approximated to the nearest hour.
- return isNightLightActivated
- ? mContext.getString(
- R.string.quick_settings_night_secondary_label_until,
- mController.getCustomEndTime().format(
- DateTimeFormatter.ofPattern(
- APPROXIMATE_HOUR_DATE_TIME_PATTERN)))
- : mContext.getString(
- R.string.quick_settings_night_secondary_label_on_at,
- mController.getCustomStartTime().format(
- DateTimeFormatter.ofPattern(
- APPROXIMATE_HOUR_DATE_TIME_PATTERN)));
+ final @StringRes int toggleTimeStringRes;
+ final LocalTime toggleTime;
+ final DateTimeFormatter toggleTimeFormat;
+
+ if (isNightLightActivated) {
+ toggleTime = mController.getCustomEndTime();
+ toggleTimeStringRes = R.string.quick_settings_night_secondary_label_until;
+ } else {
+ toggleTime = mController.getCustomStartTime();
+ toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at;
+ }
+
+ // Choose between just showing the hour or also showing the minutes (based on the
+ // user-selected toggle time). This helps reduce how much space the label takes.
+ toggleTimeFormat = DateTimeFormatter.ofPattern(
+ toggleTime.getMinute() == 0
+ ? HOUR_MINUTE_DATE_TIME_PATTERN
+ : APPROXIMATE_HOUR_DATE_TIME_PATTERN);
+
+ return mContext.getString(toggleTimeStringRes, toggleTime.format(toggleTimeFormat));
default:
// No secondary label when auto mode is disabled.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java b/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java
new file mode 100644
index 0000000..0464886
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java
@@ -0,0 +1,57 @@
+/*
+ * 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.systemui.qs.touch;
+
+/**
+ * Utility methods for overscroll damping and related effect.
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/OverScroll.java
+ */
+public class OverScroll {
+
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
+ /**
+ * This curve determines how the effect of scrolling over the limits of the page diminishes
+ * as the user pulls further and further from the bounds
+ *
+ * @param f The percentage of how much the user has overscrolled.
+ * @return A transformed percentage based on the influence curve.
+ */
+ private static float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ /**
+ * @param amount The original amount overscrolled.
+ * @param max The maximum amount that the View can overscroll.
+ * @return The dampened overscroll amount.
+ */
+ public static int dampedScroll(float amount, int max) {
+ if (Float.compare(amount, 0) == 0) return 0;
+
+ float f = amount / max;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ return Math.round(OVERSCROLL_DAMP_FACTOR * f * max);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java b/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java
new file mode 100644
index 0000000..2522052
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java
@@ -0,0 +1,356 @@
+/*
+ * 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.systemui.qs.touch;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * One dimensional scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/SwipeDetector.java
+ */
+public class SwipeDetector {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "SwipeDetector";
+
+ private int mScrollConditions;
+ public static final int DIRECTION_POSITIVE = 1 << 0;
+ public static final int DIRECTION_NEGATIVE = 1 << 1;
+ public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
+
+ private static final float ANIMATION_DURATION = 1200;
+
+ protected int mActivePointerId = INVALID_POINTER_ID;
+
+ /**
+ * The minimum release velocity in pixels per millisecond that triggers fling..
+ */
+ public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+
+ /**
+ * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
+ * Cutoff frequency is set at 10 Hz.
+ */
+ public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
+
+ /* Scroll state, this is set to true during dragging and animation. */
+ private ScrollState mState = ScrollState.IDLE;
+
+ enum ScrollState {
+ IDLE,
+ DRAGGING, // onDragStart, onDrag
+ SETTLING // onDragEnd
+ }
+
+ public static abstract class Direction {
+
+ abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+
+ /**
+ * Distance in pixels a touch can wander before we think the user is scrolling.
+ */
+ abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+ }
+
+ public static final Direction VERTICAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getY(pointerIndex) - refPoint.y;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getX(pointerIndex) - downPos.x);
+ }
+ };
+
+ public static final Direction HORIZONTAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getX(pointerIndex) - refPoint.x;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getY(pointerIndex) - downPos.y);
+ }
+ };
+
+ //------------------- ScrollState transition diagram -----------------------------------
+ //
+ // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
+ // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+ // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+ // SETTLING -> (View settled) -> IDLE
+
+ private void setState(ScrollState newState) {
+ if (DBG) {
+ Log.d(TAG, "setState:" + mState + "->" + newState);
+ }
+ // onDragStart and onDragEnd is reported ONLY on state transition
+ if (newState == ScrollState.DRAGGING) {
+ initializeDragging();
+ if (mState == ScrollState.IDLE) {
+ reportDragStart(false /* recatch */);
+ } else if (mState == ScrollState.SETTLING) {
+ reportDragStart(true /* recatch */);
+ }
+ }
+ if (newState == ScrollState.SETTLING) {
+ reportDragEnd();
+ }
+
+ mState = newState;
+ }
+
+ public boolean isDraggingOrSettling() {
+ return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+ }
+
+ /**
+ * There's no touch and there's no animation.
+ */
+ public boolean isIdleState() {
+ return mState == ScrollState.IDLE;
+ }
+
+ public boolean isSettlingState() {
+ return mState == ScrollState.SETTLING;
+ }
+
+ public boolean isDraggingState() {
+ return mState == ScrollState.DRAGGING;
+ }
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final Direction mDir;
+
+ private final float mTouchSlop;
+
+ /* Client of this gesture detector can register a callback. */
+ private final Listener mListener;
+
+ private long mCurrentMillis;
+
+ private float mVelocity;
+ private float mLastDisplacement;
+ private float mDisplacement;
+
+ private float mSubtractDisplacement;
+ private boolean mIgnoreSlopWhenSettling;
+
+ public interface Listener {
+ void onDragStart(boolean start);
+
+ boolean onDrag(float displacement, float velocity);
+
+ void onDragEnd(float velocity, boolean fling);
+ }
+
+ public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
+ this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+ }
+
+ @VisibleForTesting
+ protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
+ mTouchSlop = touchSlope;
+ mListener = l;
+ mDir = dir;
+ }
+
+ public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+ mScrollConditions = scrollDirectionFlags;
+ mIgnoreSlopWhenSettling = ignoreSlop;
+ }
+
+ private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
+ // reject cases where the angle or slop condition is not met.
+ if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
+ > Math.abs(mDisplacement)) {
+ return false;
+ }
+
+ // Check if the client is interested in scroll in current direction.
+ if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
+ ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mLastDisplacement = 0;
+ mDisplacement = 0;
+ mVelocity = 0;
+
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ setState(ScrollState.DRAGGING);
+ }
+ break;
+ //case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
+ computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
+ ev.getEventTime());
+
+ // handle state and listener calls.
+ if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+ setState(ScrollState.DRAGGING);
+ }
+ if (mState == ScrollState.DRAGGING) {
+ reportDragging();
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // These are synthetic events and there is no need to update internal values.
+ if (mState == ScrollState.DRAGGING) {
+ setState(ScrollState.SETTLING);
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ public void finishedScrolling() {
+ setState(ScrollState.IDLE);
+ }
+
+ private boolean reportDragStart(boolean recatch) {
+ mListener.onDragStart(!recatch);
+ if (DBG) {
+ Log.d(TAG, "onDragStart recatch:" + recatch);
+ }
+ return true;
+ }
+
+ private void initializeDragging() {
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ mSubtractDisplacement = 0;
+ }
+ if (mDisplacement > 0) {
+ mSubtractDisplacement = mTouchSlop;
+ } else {
+ mSubtractDisplacement = -mTouchSlop;
+ }
+ }
+
+ private boolean reportDragging() {
+ if (mDisplacement != mLastDisplacement) {
+ if (DBG) {
+ Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
+ mDisplacement, mVelocity));
+ }
+
+ mLastDisplacement = mDisplacement;
+ return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
+ }
+ return true;
+ }
+
+ private void reportDragEnd() {
+ if (DBG) {
+ Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
+ mDisplacement, mVelocity));
+ }
+ mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
+
+ }
+
+ /**
+ * Computes the damped velocity.
+ */
+ public float computeVelocity(float delta, long currentMillis) {
+ long previousMillis = mCurrentMillis;
+ mCurrentMillis = currentMillis;
+
+ float deltaTimeMillis = mCurrentMillis - previousMillis;
+ float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
+ if (Math.abs(mVelocity) < 0.001f) {
+ mVelocity = velocity;
+ } else {
+ float alpha = computeDampeningFactor(deltaTimeMillis);
+ mVelocity = interpolate(mVelocity, velocity, alpha);
+ }
+ return mVelocity;
+ }
+
+ /**
+ * Returns a time-dependent dampening factor using delta time.
+ */
+ private static float computeDampeningFactor(float deltaTime) {
+ return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
+ }
+
+ /**
+ * Returns the linear interpolation between two values
+ */
+ private static float interpolate(float from, float to, float alpha) {
+ return (1.0f - alpha) * from + alpha * to;
+ }
+
+ public static long calculateDuration(float velocity, float progressNeeded) {
+ // TODO: make these values constants after tuning.
+ float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+ float travelDistance = Math.max(0.2f, progressNeeded);
+ long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+ if (DBG) {
+ Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
+ }
+ return duration;
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 1da4deb..409c753 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -415,7 +415,7 @@
final int activityType = runningTask != null
? runningTask.configuration.windowConfiguration.getActivityType()
: ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = sSystemServicesProxy.isScreenPinningActive();
+ boolean screenPinningActive = ActivityManagerWrapper.getInstance().isLockToAppActive();
boolean isRunningTaskInHomeOrRecentsStack =
activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index ee1b091..3f6f30b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -386,8 +386,7 @@
public void toggleRecents(int growTarget) {
// Skip preloading if the task is locked
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isScreenPinningActive()) {
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
return;
}
@@ -409,8 +408,8 @@
MutableBoolean isHomeStackVisible = new MutableBoolean(true);
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!launchState.launchedWithAltTab) {
@@ -466,8 +465,7 @@
public void preloadRecents() {
// Skip preloading if the task is locked
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.isScreenPinningActive()) {
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
return;
}
@@ -481,6 +479,7 @@
// RecentsActivity) only if there is a task to animate to. Post this to ensure that we
// don't block the touch feedback on the nav bar button which triggers this.
mHandler.post(() -> {
+ SystemServicesProxy ssp = Recents.getSystemServices();
if (!ssp.isRecentsActivityVisible(null)) {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SwipeUpOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/recents/SwipeUpOnboarding.java
rename to packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index b2472bf..0ff8b08 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SwipeUpOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -16,13 +16,11 @@
package com.android.systemui.recents;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import android.annotation.TargetApi;
import android.app.ActivityManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -31,6 +29,8 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -41,6 +41,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.systemui.OverviewProxyService;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
@@ -50,9 +51,9 @@
* Shows onboarding for the new recents interaction in P (codenamed quickstep).
*/
@TargetApi(Build.VERSION_CODES.P)
-public class SwipeUpOnboarding {
+public class RecentsOnboarding {
- private static final String TAG = "SwipeUpOnboarding";
+ private static final String TAG = "RecentsOnboarding";
private static final boolean RESET_PREFS_FOR_DEBUG = false;
private static final long SHOW_DELAY_MS = 500;
private static final long SHOW_HIDE_DURATION_MS = 300;
@@ -61,6 +62,7 @@
private final Context mContext;
private final WindowManager mWindowManager;
+ private final OverviewProxyService mOverviewProxyService;
private final View mLayout;
private final TextView mTextView;
private final ImageView mDismissView;
@@ -113,11 +115,12 @@
}
};
- public SwipeUpOnboarding(Context context) {
+ public RecentsOnboarding(Context context, OverviewProxyService overviewProxyService) {
mContext = context;
+ mOverviewProxyService = overviewProxyService;
final Resources res = context.getResources();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_swipe_up_onboarding, null);
+ mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null);
mTextView = mLayout.findViewById(R.id.onboarding_text);
mDismissView = mLayout.findViewById(R.id.dismiss);
mDarkBackgroundColor = res.getColor(android.R.color.background_dark);
@@ -135,25 +138,25 @@
mDismissView.setOnClickListener(v -> hide(true));
if (RESET_PREFS_FOR_DEBUG) {
- Prefs.putBoolean(mContext, Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, false);
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
}
}
public void onConnectedToLauncher() {
- boolean alreadyLearnedSwipeUpForRecents = Prefs.getBoolean(mContext,
- Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, false);
- if (!mTaskListenerRegistered && !alreadyLearnedSwipeUpForRecents) {
+ boolean alreadySeenRecentsOnboarding = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
+ if (!mTaskListenerRegistered && !alreadySeenRecentsOnboarding) {
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
mTaskListenerRegistered = true;
}
}
public void onRecentsAnimationStarted() {
- boolean alreadyLearnedSwipeUpForRecents = Prefs.getBoolean(mContext,
- Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, false);
- if (!alreadyLearnedSwipeUpForRecents) {
- Prefs.putBoolean(mContext, Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, true);
+ boolean alreadySeenRecentsOnboarding = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
+ if (!alreadySeenRecentsOnboarding) {
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, true);
onDisconnectedFromLauncher();
}
}
@@ -173,6 +176,12 @@
}
public void show() {
+ CharSequence onboardingText = mOverviewProxyService.getOnboardingText();
+ if (TextUtils.isEmpty(onboardingText)) {
+ Log.w(TAG, "Unable to get onboarding text");
+ return;
+ }
+ mTextView.setText(onboardingText);
// Only show in portrait.
int orientation = mContext.getResources().getConfiguration().orientation;
if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -239,7 +248,7 @@
flags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("SwipeUpOnboarding");
+ lp.setTitle("RecentsOnboarding");
lp.gravity = Gravity.BOTTOM;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 613d9fb..93fd34a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -379,29 +379,6 @@
}
/**
- * Returns a global setting.
- */
- public int getGlobalSetting(Context context, String setting) {
- ContentResolver cr = context.getContentResolver();
- return Settings.Global.getInt(cr, setting, 0);
- }
-
- /**
- * Returns a system setting.
- */
- public int getSystemSetting(Context context, String setting) {
- ContentResolver cr = context.getContentResolver();
- return Settings.System.getInt(cr, setting, 0);
- }
-
- /**
- * Returns a system property.
- */
- public String getSystemProperty(String key) {
- return SystemProperties.get(key);
- }
-
- /**
* Returns the smallest width/height.
*/
public int getDeviceSmallestWidth() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 5be2900..3cc3273 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -95,6 +95,7 @@
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2187,8 +2188,7 @@
private void readSystemFlags() {
SystemServicesProxy ssp = Recents.getSystemServices();
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isLockToAppEnabled();
}
private void updateStackActionButtonVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f5f62b85..fa177f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -156,7 +156,7 @@
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
- default void showChargingAnimation(int batteryLevel) { }
+ default void showWirelessChargingAnimation(int batteryLevel) { }
default void onRotationProposal(int rotation, boolean isValid) { }
@@ -497,7 +497,7 @@
}
@Override
- public void showChargingAnimation(int batteryLevel) {
+ public void showWirelessChargingAnimation(int batteryLevel) {
mHandler.removeMessages(MSG_SHOW_CHARGING_ANIMATION);
mHandler.obtainMessage(MSG_SHOW_CHARGING_ANIMATION, batteryLevel, 0)
.sendToTarget();
@@ -784,12 +784,12 @@
break;
case MSG_SHOW_CHARGING_ANIMATION:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showChargingAnimation(msg.arg1);
+ mCallbacks.get(i).showWirelessChargingAnimation(msg.arg1);
}
break;
case MSG_SHOW_PINNING_TOAST_ENTER_EXIT:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showPinningEnterExitToast(msg.arg1 != 0);
+ mCallbacks.get(i).showPinningEnterExitToast((Boolean) msg.obj);
}
break;
case MSG_SHOW_PINNING_TOAST_ESCAPE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0a12be4..b7a1500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -44,6 +46,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockIcon;
@@ -192,7 +195,7 @@
if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
hideTransientIndication();
}
- updateIndication();
+ updateIndication(false);
} else if (!visible) {
// If we unlock and return to keyguard quickly, previous error should not be shown
hideTransientIndication();
@@ -204,7 +207,7 @@
*/
public void setRestingIndication(String restingIndication) {
mRestingIndication = restingIndication;
- updateIndication();
+ updateIndication(false);
}
/**
@@ -265,7 +268,8 @@
mWakeLock.setAcquired(true);
hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
}
- updateIndication();
+
+ updateIndication(false);
}
/**
@@ -275,11 +279,11 @@
if (mTransientIndication != null) {
mTransientIndication = null;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- updateIndication();
+ updateIndication(false);
}
}
- protected final void updateIndication() {
+ protected final void updateIndication(boolean animate) {
if (TextUtils.isEmpty(mTransientIndication)) {
mWakeLock.setAcquired(false);
}
@@ -295,7 +299,35 @@
mTextView.switchIndication(mTransientIndication);
} else if (mPowerPluggedIn) {
String indication = computePowerIndication();
- mTextView.switchIndication(indication);
+ if (animate) {
+ int yTranslation = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_distance);
+ int animateUpDuration = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_duration_up);
+ int animateDownDuration = mContext.getResources().getInteger(
+ R.integer.wired_charging_aod_text_animation_duration_down);
+ mTextView.animate()
+ .translationYBy(yTranslation)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(animateUpDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mTextView.switchIndication(indication);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTextView.animate()
+ .setDuration(animateDownDuration)
+ .setInterpolator(Interpolators.BOUNCE)
+ .translationYBy(-1 * yTranslation)
+ .setListener(null);
+ }
+ });
+ } else {
+ mTextView.switchIndication(indication);
+ }
+
} else {
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
@@ -390,7 +422,7 @@
public void onReceive(Context context, Intent intent) {
mHandler.post(() -> {
if (mVisible) {
- updateIndication();
+ updateIndication(false);
}
});
}
@@ -412,7 +444,7 @@
return;
}
mDozing = dozing;
- updateIndication();
+ updateIndication(false);
updateDisclosure();
}
@@ -445,7 +477,7 @@
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
mBatteryLevel = status.level;
- updateIndication();
+ updateIndication(!wasPluggedIn && mPowerPluggedIn);
if (mDozing) {
if (!wasPluggedIn && mPowerPluggedIn) {
showTransientIndication(computePowerIndication());
@@ -551,7 +583,7 @@
@Override
public void onUserUnlocked() {
if (mVisible) {
- updateIndication();
+ updateIndication(false);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index d6beb7f..ab89a52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -249,6 +249,9 @@
(GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
gradientDrawable.setXfermode(
running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
+ // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
+ // spot during animation anyways.
+ gradientDrawable.setAntiAlias(!running);
}
if (!mExpandAnimationRunning) {
setDrawableAlpha(mDrawableAlpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index bcdc269..47fa1d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -272,12 +272,20 @@
* package-specific override.
*/
public boolean shouldHideNotifications(String key) {
+ if (mEntryManager == null) {
+ Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+ return true;
+ }
return isLockscreenPublicMode(mCurrentUserId)
&& mEntryManager.getNotificationData().getVisibilityOverride(key) ==
Notification.VISIBILITY_SECRET;
}
public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+ if (mEntryManager == null) {
+ Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+ return false;
+ }
return mShowLockscreenNotifications
&& !mEntryManager.getNotificationData().isAmbient(sbn.getKey());
}
@@ -397,6 +405,10 @@
}
private boolean packageHasVisibilityOverride(String key) {
+ if (mEntryManager == null) {
+ Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+ return true;
+ }
return mEntryManager.getNotificationData().getVisibilityOverride(key) ==
Notification.VISIBILITY_PRIVATE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index 8227b77..d3a325d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -21,6 +21,8 @@
import android.view.View;
import android.widget.ImageView;
+import com.android.internal.widget.MessagingImageMessage;
+import com.android.internal.widget.MessagingMessage;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -117,13 +119,15 @@
@Override
protected boolean transformScale(TransformState otherState) {
- return true;
+ return sameAs(otherState);
}
@Override
public void recycle() {
super.recycle();
- sInstancePool.release(this);
+ if (getClass() == ImageTransformState.class) {
+ sInstancePool.release(this);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java
new file mode 100644
index 0000000..b97995d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingImageTransformState.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.util.Pools;
+import android.view.View;
+
+import com.android.internal.widget.MessagingImageMessage;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.ViewTransformationHelper;
+
+/**
+ * A transform state of a image view.
+*/
+public class MessagingImageTransformState extends ImageTransformState {
+ private static Pools.SimplePool<MessagingImageTransformState> sInstancePool
+ = new Pools.SimplePool<>(40);
+ private static final int START_ACTUAL_WIDTH = R.id.transformation_start_actual_width;
+ private static final int START_ACTUAL_HEIGHT = R.id.transformation_start_actual_height;
+ private MessagingImageMessage mImageMessage;
+
+ @Override
+ public void initFrom(View view, TransformInfo transformInfo) {
+ super.initFrom(view, transformInfo);
+ mImageMessage = (MessagingImageMessage) view;
+ }
+
+ @Override
+ protected boolean sameAs(TransformState otherState) {
+ if (super.sameAs(otherState)) {
+ return true;
+ }
+ if (otherState instanceof MessagingImageTransformState) {
+ MessagingImageTransformState otherMessage = (MessagingImageTransformState) otherState;
+ return mImageMessage.sameAs(otherMessage.mImageMessage);
+ }
+ return false;
+ }
+
+ public static MessagingImageTransformState obtain() {
+ MessagingImageTransformState instance = sInstancePool.acquire();
+ if (instance != null) {
+ return instance;
+ }
+ return new MessagingImageTransformState();
+ }
+
+ @Override
+ protected boolean transformScale(TransformState otherState) {
+ return false;
+ }
+
+ @Override
+ protected void transformViewFrom(TransformState otherState, int transformationFlags,
+ ViewTransformationHelper.CustomTransformation customTransformation,
+ float transformationAmount) {
+ super.transformViewFrom(otherState, transformationFlags, customTransformation,
+ transformationAmount);
+ float interpolatedValue = mDefaultInterpolator.getInterpolation(
+ transformationAmount);
+ if (otherState instanceof MessagingImageTransformState && sameAs(otherState)) {
+ MessagingImageMessage otherMessage
+ = ((MessagingImageTransformState) otherState).mImageMessage;
+ if (transformationAmount == 0.0f) {
+ setStartActualWidth(otherMessage.getActualWidth());
+ setStartActualHeight(otherMessage.getActualHeight());
+ }
+ float startActualWidth = getStartActualWidth();
+ mImageMessage.setActualWidth(
+ (int) NotificationUtils.interpolate(startActualWidth,
+ mImageMessage.getStaticWidth(),
+ interpolatedValue));
+ float startActualHeight = getStartActualHeight();
+ mImageMessage.setActualHeight(
+ (int) NotificationUtils.interpolate(startActualHeight,
+ mImageMessage.getHeight(),
+ interpolatedValue));
+ }
+ }
+
+ public int getStartActualWidth() {
+ Object tag = mTransformedView.getTag(START_ACTUAL_WIDTH);
+ return tag == null ? -1 : (int) tag;
+ }
+
+ public void setStartActualWidth(int actualWidth) {
+ mTransformedView.setTag(START_ACTUAL_WIDTH, actualWidth);
+ }
+
+ public int getStartActualHeight() {
+ Object tag = mTransformedView.getTag(START_ACTUAL_HEIGHT);
+ return tag == null ? -1 : (int) tag;
+ }
+
+ public void setStartActualHeight(int actualWidth) {
+ mTransformedView.setTag(START_ACTUAL_HEIGHT, actualWidth);
+ }
+
+ @Override
+ public void recycle() {
+ super.recycle();
+ if (getClass() == MessagingImageTransformState.class) {
+ sInstancePool.release(this);
+ }
+ }
+
+ @Override
+ protected void resetTransformedView() {
+ super.resetTransformedView();
+ mImageMessage.setActualWidth(mImageMessage.getStaticWidth());
+ mImageMessage.setActualHeight(mImageMessage.getHeight());
+ }
+
+ @Override
+ protected void reset() {
+ super.reset();
+ mImageMessage = null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 113118a..314a31d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -22,6 +22,7 @@
import android.view.ViewGroup;
import com.android.internal.widget.MessagingGroup;
+import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingLayout;
import com.android.internal.widget.MessagingLinearLayout;
import com.android.internal.widget.MessagingMessage;
@@ -30,6 +31,7 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* A transform state of the action list
@@ -156,6 +158,7 @@
}
appear(ownGroup.getAvatar(), transformationAmount);
appear(ownGroup.getSenderView(), transformationAmount);
+ appear(ownGroup.getIsolatedMessage(), transformationAmount);
setClippingDeactivated(ownGroup.getSenderView(), true);
setClippingDeactivated(ownGroup.getAvatar(), true);
}
@@ -187,12 +190,13 @@
}
disappear(ownGroup.getAvatar(), transformationAmount);
disappear(ownGroup.getSenderView(), transformationAmount);
+ disappear(ownGroup.getIsolatedMessage(), transformationAmount);
setClippingDeactivated(ownGroup.getSenderView(), true);
setClippingDeactivated(ownGroup.getAvatar(), true);
}
private void appear(View child, float transformationAmount) {
- if (child.getVisibility() == View.GONE) {
+ if (child == null || child.getVisibility() == View.GONE) {
return;
}
TransformState ownState = TransformState.createFrom(child, mTransformInfo);
@@ -201,7 +205,7 @@
}
private void disappear(View child, float transformationAmount) {
- if (child.getVisibility() == View.GONE) {
+ if (child == null || child.getVisibility() == View.GONE) {
return;
}
TransformState ownState = TransformState.createFrom(child, mTransformInfo);
@@ -224,22 +228,24 @@
private void transformGroups(MessagingGroup ownGroup, MessagingGroup otherGroup,
float transformationAmount, boolean to) {
+ boolean useLinearTransformation =
+ otherGroup.getIsolatedMessage() == null && !mTransformInfo.isAnimating();
transformView(transformationAmount, to, ownGroup.getSenderView(), otherGroup.getSenderView(),
- true /* sameAsAny */);
+ true /* sameAsAny */, useLinearTransformation);
transformView(transformationAmount, to, ownGroup.getAvatar(), otherGroup.getAvatar(),
- true /* sameAsAny */);
- MessagingLinearLayout ownMessages = ownGroup.getMessageContainer();
- MessagingLinearLayout otherMessages = otherGroup.getMessageContainer();
+ true /* sameAsAny */, useLinearTransformation);
+ List<MessagingMessage> ownMessages = ownGroup.getMessages();
+ List<MessagingMessage> otherMessages = otherGroup.getMessages();
float previousTranslation = 0;
- for (int i = 0; i < ownMessages.getChildCount(); i++) {
- View child = ownMessages.getChildAt(ownMessages.getChildCount() - 1 - i);
+ for (int i = 0; i < ownMessages.size(); i++) {
+ View child = ownMessages.get(ownMessages.size() - 1 - i).getView();
if (isGone(child)) {
continue;
}
- int otherIndex = otherMessages.getChildCount() - 1 - i;
+ int otherIndex = otherMessages.size() - 1 - i;
View otherChild = null;
if (otherIndex >= 0) {
- otherChild = otherMessages.getChildAt(otherIndex);
+ otherChild = otherMessages.get(otherIndex).getView();
if (isGone(otherChild)) {
otherChild = null;
}
@@ -252,7 +258,12 @@
transformationAmount = 1.0f - transformationAmount;
}
}
- transformView(transformationAmount, to, child, otherChild, false /* sameAsAny */);
+ transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
+ useLinearTransformation);
+ if (transformationAmount == 0.0f
+ && otherGroup.getIsolatedMessage() == otherChild) {
+ ownGroup.setTransformingImages(true);
+ }
if (otherChild == null) {
child.setTranslationY(previousTranslation);
setClippingDeactivated(child, true);
@@ -264,12 +275,13 @@
previousTranslation = child.getTranslationY();
}
}
+ ownGroup.updateClipRect();
}
private void transformView(float transformationAmount, boolean to, View ownView,
- View otherView, boolean sameAsAny) {
+ View otherView, boolean sameAsAny, boolean useLinearTransformation) {
TransformState ownState = TransformState.createFrom(ownView, mTransformInfo);
- if (!mTransformInfo.isAnimating()) {
+ if (useLinearTransformation) {
ownState.setDefaultInterpolator(Interpolators.LINEAR);
}
ownState.setIsSameAsAnyView(sameAsAny);
@@ -339,11 +351,15 @@
if (!isGone(ownGroup)) {
MessagingLinearLayout ownMessages = ownGroup.getMessageContainer();
for (int j = 0; j < ownMessages.getChildCount(); j++) {
- MessagingMessage child = (MessagingMessage) ownMessages.getChildAt(j);
+ View child = ownMessages.getChildAt(j);
setVisible(child, visible, force);
}
setVisible(ownGroup.getAvatar(), visible, force);
setVisible(ownGroup.getSenderView(), visible, force);
+ MessagingImageMessage isolatedMessage = ownGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ setVisible(isolatedMessage, visible, force);
+ }
}
}
}
@@ -375,11 +391,17 @@
}
resetTransformedView(ownGroup.getAvatar());
resetTransformedView(ownGroup.getSenderView());
+ MessagingImageMessage isolatedMessage = ownGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ resetTransformedView(isolatedMessage);
+ }
setClippingDeactivated(ownGroup.getAvatar(), false);
setClippingDeactivated(ownGroup.getSenderView(), false);
ownGroup.setTranslationY(0);
ownGroup.getMessageContainer().setTranslationY(0);
}
+ ownGroup.setTransformingImages(false);
+ ownGroup.updateClipRect();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 918b6ed..fc8ceb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -26,6 +26,7 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingPropertyAnimator;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Interpolators;
@@ -80,7 +81,7 @@
private boolean mSameAsAny;
private float mTransformationEndY = UNDEFINED;
private float mTransformationEndX = UNDEFINED;
- private Interpolator mDefaultInterpolator = Interpolators.FAST_OUT_SLOW_IN;
+ protected Interpolator mDefaultInterpolator = Interpolators.FAST_OUT_SLOW_IN;
public void initFrom(View view, TransformInfo transformInfo) {
mTransformedView = view;
@@ -131,7 +132,7 @@
transformViewFrom(otherState, TRANSFORM_Y, null, transformationAmount);
}
- private void transformViewFrom(TransformState otherState, int transformationFlags,
+ protected void transformViewFrom(TransformState otherState, int transformationFlags,
ViewTransformationHelper.CustomTransformation customTransformation,
float transformationAmount) {
final View transformedView = mTransformedView;
@@ -449,6 +450,11 @@
result.initFrom(view, transformInfo);
return result;
}
+ if (view instanceof MessagingImageMessage) {
+ MessagingImageTransformState result = MessagingImageTransformState.obtain();
+ result.initFrom(view, transformInfo);
+ return result;
+ }
if (view instanceof ImageView) {
ImageTransformState result = ImageTransformState.obtain();
result.initFrom(view, transformInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index c45c538..aba5cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -81,7 +81,6 @@
@Override
public boolean release(@NonNull HeadsUpEntryPhone instance) {
- instance.reset();
mPoolObjects.push(instance);
return true;
}
@@ -125,10 +124,7 @@
*/
public boolean shouldSwallowClick(@NonNull String key) {
HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
- if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
- return true;
- }
- return false;
+ return entry != null && mClock.currentTimeMillis() < entry.postTime;
}
public void onExpandingFinished() {
@@ -212,9 +208,9 @@
@VisibleForTesting
public void removeMinimumDisplayTimeForTesting() {
- mMinimumDisplayTime = 1;
- mHeadsUpNotificationDecay = 1;
- mTouchAcceptanceDelay = 1;
+ mMinimumDisplayTime = 0;
+ mHeadsUpNotificationDecay = 0;
+ mTouchAcceptanceDelay = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -321,6 +317,7 @@
@Override
protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
+ entry.reset();
mEntryPool.release((HeadsUpEntryPhone) entry);
}
@@ -337,21 +334,18 @@
}
///////////////////////////////////////////////////////////////////////////////////////////////
- // Protected utility methods:
+ // Private utility methods:
@Nullable
- protected HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
+ private HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
return (HeadsUpEntryPhone) getHeadsUpEntry(key);
}
@Nullable
- protected HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
+ private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
return (HeadsUpEntryPhone) getTopHeadsUpEntry();
}
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Private utility methods:
-
private boolean wasShownLongEnough(@NonNull String key) {
if (mSwipedOutKeys.contains(key)) {
// We always instantly dismiss views being manually swiped out.
@@ -361,10 +355,7 @@
HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
- if (headsUpEntry != topEntry) {
- return true;
- }
- return headsUpEntry.wasShownLongEnough();
+ return headsUpEntry != topEntry || headsUpEntry.wasShownLongEnough();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 242be71..1239a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -591,11 +591,9 @@
ButtonDispatcher backButton = mNavigationBarView.getBackButton();
backButton.setLongClickable(true);
- backButton.setOnLongClickListener(this::onLongPressBackRecents);
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
- homeButton.setOnLongClickListener(this::onHomeLongClick);
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
@@ -605,6 +603,7 @@
ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
+ updateScreenPinningGestures();
}
private boolean onHomeTouch(View v, MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 0d36efd..8d14db7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -16,37 +16,37 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.OverviewProxyService.TAG_OPS;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.Dependency;
import com.android.systemui.OverviewProxyService;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.OverviewProxyService.TAG_OPS;
-
/**
* Class to detect gestures on the navigation bar.
*/
@@ -84,8 +84,16 @@
private int mTouchDownY;
private boolean mDownOnRecents;
private VelocityTracker mVelocityTracker;
- private OverviewProxyService mOverviewEventSender = Dependency.get(OverviewProxyService.class);
+ private OverviewProxyService mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+ @Override
+ public void onRecentsAnimationStarted() {
+ mRecentsAnimationStarted = true;
+ mQuickScrubController.cancelQuickSwitch();
+ }
+ };
+ private boolean mRecentsAnimationStarted;
private boolean mDockWindowEnabled;
private boolean mDockWindowTouchSlopExceeded;
private int mDragMode;
@@ -97,10 +105,12 @@
mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
mQuickScrubController = new QuickScrubController(context);
Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
}
public void destroy() {
Dependency.get(TunerService.class).removeTunable(this);
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
}
public void setComponents(RecentsComponent recentsComponent, Divider divider,
@@ -117,7 +127,7 @@
}
private boolean proxyMotionEvents(MotionEvent event) {
- final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ final IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
if (overviewProxy != null) {
mNavigationBarView.requestUnbufferedDispatch(event);
event.transform(mTransformGlobalMatrix);
@@ -146,6 +156,19 @@
mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
+ mRecentsAnimationStarted = false;
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ // If the overview proxy service has not started the recents animation then clean up
+ // after it to ensure that the nav bar buttons still work
+ if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) {
+ try {
+ ActivityManager.getService().cancelRecentsAnimation();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not cancel recents animation", e);
+ }
+ }
break;
}
}
@@ -154,11 +177,25 @@
proxyMotionEvents(event);
return false;
}
- return (mDockWindowEnabled && interceptDockWindowEvent(event));
+ return (mDockWindowEnabled && interceptDockWindowEvent(event)) || mRecentsAnimationStarted;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ // The same down event was just sent on intercept and therefore can be ignored here
+ boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
+ && mOverviewProxyService.getProxy() != null;
+ boolean result = mStatusBar.isPresenterFullyCollapsed()
+ && (mQuickScrubController.onTouchEvent(event)
+ || ignoreProxyDownEvent
+ || proxyMotionEvents(event));
+ if (mDockWindowEnabled) {
+ result |= handleDockWindowEvent(event);
+ }
+ return result || mRecentsAnimationStarted;
}
public void onDraw(Canvas canvas) {
- if (mOverviewEventSender.getProxy() != null) {
+ if (mOverviewProxyService.getProxy() != null) {
mQuickScrubController.onDraw(canvas);
}
}
@@ -307,20 +344,6 @@
return DRAG_MODE_RECENTS;
}
- public boolean onTouchEvent(MotionEvent event) {
- // The same down event was just sent on intercept and therefore can be ignored here
- boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
- && mOverviewEventSender.getProxy() != null;
- boolean result = mStatusBar.isPresenterFullyCollapsed()
- && (mQuickScrubController.onTouchEvent(event)
- || ignoreProxyDownEvent
- || proxyMotionEvents(event));
- if (mDockWindowEnabled) {
- result |= handleDockWindowEvent(event);
- }
- return result;
- }
-
@Override
public void onTuningChanged(String key, String newValue) {
switch (key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cd220a7..af0afbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -50,14 +50,13 @@
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.OverviewProxyService;
-import com.android.systemui.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavGesture;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.recents.SwipeUpOnboarding;
+import com.android.systemui.recents.RecentsOnboarding;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -104,7 +103,6 @@
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
private final OverviewProxyService mOverviewProxyService;
- private boolean mRecentsAnimationStarted;
// workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -126,7 +124,7 @@
private NavigationBarInflaterView mNavigationInflaterView;
private RecentsComponent mRecentsComponent;
private Divider mDivider;
- private SwipeUpOnboarding mSwipeUpOnboarding;
+ private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
private class NavTransitionListener implements TransitionListener {
@@ -236,7 +234,7 @@
new ButtonDispatcher(R.id.rotate_suggestion));
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
- mSwipeUpOnboarding = new SwipeUpOnboarding(context);
+ mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
}
public BarTransitions getBarTransitions() {
@@ -264,9 +262,8 @@
}
public void setRecentsAnimationStarted(boolean started) {
- mRecentsAnimationStarted = started;
- if (mSwipeUpOnboarding != null) {
- mSwipeUpOnboarding.onRecentsAnimationStarted();
+ if (mRecentsOnboarding != null) {
+ mRecentsOnboarding.onRecentsAnimationStarted();
}
}
@@ -277,30 +274,16 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return mGestureHelper.onInterceptTouchEvent(event);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
- return mRecentsAnimationStarted || super.onTouchEvent(event);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- mRecentsAnimationStarted = false;
- } else if (action == MotionEvent.ACTION_UP) {
- // If the overview proxy service has not started the recents animation then clean up
- // after it to ensure that the nav bar buttons still work
- if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) {
- try {
- ActivityManager.getService().cancelRecentsAnimation();
- } catch (RemoteException e) {
- Log.e(TAG, "Could not cancel recents animation");
- }
- }
- }
- return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
+ return super.onTouchEvent(event);
}
public void abortCurrentGesture() {
@@ -674,8 +657,8 @@
if (mGestureHelper != null) {
mGestureHelper.onDarkIntensityChange(intensity);
}
- if (mSwipeUpOnboarding != null) {
- mSwipeUpOnboarding.setContentDarkIntensity(intensity);
+ if (mRecentsOnboarding != null) {
+ mRecentsOnboarding.setContentDarkIntensity(intensity);
}
}
@@ -793,7 +776,7 @@
updateTaskSwitchHelper();
updateIcons(getContext(), mConfiguration, newConfig);
updateRecentsIcon();
- mSwipeUpOnboarding.onConfigurationChanged(newConfig);
+ mRecentsOnboarding.onConfigurationChanged(newConfig);
if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi
|| mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) {
// If car mode or density changes, we need to reset the icons.
@@ -897,9 +880,9 @@
private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) {
if (connectedToOverviewProxy) {
- mSwipeUpOnboarding.onConnectedToLauncher();
+ mRecentsOnboarding.onConnectedToLauncher();
} else {
- mSwipeUpOnboarding.onDisconnectedFromLauncher();
+ mRecentsOnboarding.onDisconnectedFromLauncher();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index 001a1a2..dc0835e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -63,7 +63,7 @@
private static final String TAG = "QuickScrubController";
private static final int QUICK_SWITCH_FLING_VELOCITY = 0;
private static final int ANIM_DURATION_MS = 200;
- private static final long LONG_PRESS_DELAY_MS = 150;
+ private static final long LONG_PRESS_DELAY_MS = 225;
/**
* For quick step, set a damping value to allow the button to stick closer its origin position
@@ -76,6 +76,7 @@
private boolean mDraggingActive;
private boolean mQuickScrubActive;
+ private boolean mAllowQuickSwitch;
private float mDownOffset;
private float mTranslation;
private int mTouchDownX;
@@ -95,7 +96,6 @@
private final Paint mTrackPaint = new Paint();
private final int mScrollTouchSlop;
private final OverviewProxyService mOverviewEventSender;
- private final Display mDisplay;
private final int mTrackThickness;
private final int mTrackPadding;
private final ValueAnimator mTrackAnimator;
@@ -137,7 +137,8 @@
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
- if (!isQuickScrubEnabled() || mQuickScrubActive) {
+ if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch ||
+ !mHomeButtonRect.contains(mTouchDownX, mTouchDownY)) {
return false;
}
float velocityX = mIsRTL ? -velX : velX;
@@ -158,16 +159,15 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to send start of quick switch.", e);
}
+ return true;
}
- return true;
+ return false;
}
};
public QuickScrubController(Context context) {
mContext = context;
mScrollTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mDisplay = ((WindowManager) context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
mGestureDetector = new GestureDetector(mContext, mGestureListener);
mTrackThickness = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_thickness);
@@ -189,6 +189,10 @@
mNavigationBarView = navigationBarView;
}
+ /**
+ * @return true if we want to intercept touch events for quick scrub/switch and prevent proxying
+ * the event to the overview service.
+ */
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
@@ -197,7 +201,26 @@
homeButton.setDelayTouchFeedback(false);
return false;
}
- mGestureDetector.onTouchEvent(event);
+
+ return handleTouchEvent(event);
+ }
+
+ /**
+ * @return true if we want to handle touch events for quick scrub/switch and prevent proxying
+ * the event to the overview service.
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return handleTouchEvent(event);
+ }
+
+ private boolean handleTouchEvent(MotionEvent event) {
+ final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
+ if (mGestureDetector.onTouchEvent(event)) {
+ // If the fling has been handled on UP, then skip proxying the UP
+ return true;
+ }
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
@@ -212,6 +235,7 @@
homeButton.setDelayTouchFeedback(false);
mTouchDownX = mTouchDownY = -1;
}
+ mAllowQuickSwitch = true;
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -240,8 +264,9 @@
offset = pos - mTrackRect.left;
trackSize = mTrackRect.width();
}
- // Do not start scrubbing when dragging in the perpendicular direction
- if (!mDraggingActive && exceededPerpendicularTouchSlop) {
+ // Do not start scrubbing when dragging in the perpendicular direction if we
+ // haven't already started quickscrub
+ if (!mDraggingActive && !mQuickScrubActive && exceededPerpendicularTouchSlop) {
mHandler.removeCallbacksAndMessages(null);
return false;
}
@@ -341,14 +366,6 @@
}
@Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- endQuickScrub();
- }
- return false;
- }
-
- @Override
public void setBarState(boolean isVertical, boolean isRTL) {
mIsVertical = isVertical;
mIsRTL = isRTL;
@@ -403,6 +420,11 @@
mDraggingActive = false;
}
+ public void cancelQuickSwitch() {
+ mAllowQuickSwitch = false;
+ mHandler.removeCallbacks(mLongPressRunnable);
+ }
+
private int getDimensionPixelSize(Context context, @DimenRes int resId) {
return context.getResources().getDimensionPixelSize(resId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 3b394dd..2b16e74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -30,6 +30,7 @@
import android.os.Trace;
import android.util.Log;
import android.util.MathUtils;
+import android.view.Choreographer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -111,7 +112,6 @@
protected static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY;
static final int TAG_KEY_ANIM = R.id.scrim;
- static final int TAG_KEY_ANIM_BLANK = R.id.scrim_blanking;
private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
@@ -166,6 +166,7 @@
private boolean mScreenBlankingCallbackCalled;
private Callback mCallback;
private boolean mWallpaperSupportsAmbientMode;
+ private Choreographer.FrameCallback mPendingFrameCallback;
private final WakeLock mWakeLock;
private boolean mWakeLockHeld;
@@ -230,6 +231,7 @@
final ScrimState oldState = mState;
mState = state;
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex());
if (mCallback != null) {
mCallback.onCancelled();
@@ -247,6 +249,11 @@
mCurrentInFrontAlpha = state.getFrontAlpha();
mCurrentBehindAlpha = state.getBehindAlpha();
+ if (mPendingFrameCallback != null) {
+ Choreographer.getInstance().removeFrameCallback(mPendingFrameCallback);
+ mPendingFrameCallback = null;
+ }
+
// Showing/hiding the keyguard means that scrim colors have to be switched, not necessary
// to do the same when you're just showing the brightness mirror.
mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR;
@@ -275,13 +282,18 @@
mWallpaperVisibilityTimedOut = false;
}
- if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
- scheduleUpdate();
- } else {
+ if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
// In case the user isn't unlocked, make sure to delay a bit because the system is hosed
// with too many things at this case, in order to not skip the initial frames.
mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD) {
+ // Execute first frame immediately when display was completely off.
+ // Scheduling a frame isn't enough because the system may aggressively enter doze,
+ // delaying callbacks or never triggering them until the power button is pressed.
+ onPreDraw();
+ } else {
+ scheduleUpdate();
}
}
@@ -686,11 +698,12 @@
}
}
- final boolean blankingInProgress = mScrimInFront.getTag(TAG_KEY_ANIM_BLANK) != null;
- if (mBlankScreen || blankingInProgress) {
- if (!blankingInProgress) {
- blankDisplay();
- }
+ if (mPendingFrameCallback != null) {
+ // Display is off and we're waiting.
+ return;
+ } else if (mBlankScreen) {
+ // Need to blank the display before continuing.
+ blankDisplay();
return;
} else if (!mScreenBlankingCallbackCalled) {
// Not blanking the screen. Letting the callback know that we're ready
@@ -744,45 +757,33 @@
}
private void blankDisplay() {
- final float initialAlpha = mScrimInFront.getViewAlpha();
- final int initialTint = mScrimInFront.getTint();
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- anim.addUpdateListener(animation -> {
- final float amount = (float) animation.getAnimatedValue();
- float animAlpha = MathUtils.lerp(initialAlpha, 1, amount);
- int animTint = ColorUtils.blendARGB(initialTint, Color.BLACK, amount);
- updateScrimColor(mScrimInFront, animAlpha, animTint);
- dispatchScrimsVisible();
- });
- anim.setInterpolator(getInterpolator());
- anim.setDuration(mDozeParameters.getPulseInDuration());
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCallback != null) {
- mCallback.onDisplayBlanked();
- mScreenBlankingCallbackCalled = true;
- }
- Runnable blankingCallback = () -> {
- mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, null);
- mBlankScreen = false;
- // Try again.
- updateScrims();
- };
+ updateScrimColor(mScrimInFront, 1, Color.BLACK);
- // Setting power states can happen after we push out the frame. Make sure we
- // stay fully opaque until the power state request reaches the lower levels.
- getHandler().postDelayed(blankingCallback, 100);
-
+ // Notify callback that the screen is completely black and we're
+ // ready to change the display power mode
+ mPendingFrameCallback = frameTimeNanos -> {
+ if (mCallback != null) {
+ mCallback.onDisplayBlanked();
+ mScreenBlankingCallbackCalled = true;
}
- });
- anim.start();
- mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, anim);
- // Finish animation if we're already at its final state
- if (initialAlpha == 1 && mScrimInFront.getTint() == Color.BLACK) {
- anim.end();
- }
+ Runnable blankingCallback = () -> {
+ mPendingFrameCallback = null;
+ mBlankScreen = false;
+ // Try again.
+ updateScrims();
+ };
+
+ // Setting power states can happen after we push out the frame. Make sure we
+ // stay fully opaque until the power state request reaches the lower levels.
+ getHandler().postDelayed(blankingCallback, 100);
+ };
+ doOnTheNextFrame(mPendingFrameCallback);
+ }
+
+ @VisibleForTesting
+ protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
+ Choreographer.getInstance().postFrameCallback(callback);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index fa2c1b3..381e4af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -31,12 +31,12 @@
/**
* Initial state.
*/
- UNINITIALIZED,
+ UNINITIALIZED(-1),
/**
* On the lock screen.
*/
- KEYGUARD {
+ KEYGUARD(0) {
@Override
public void prepare(ScrimState previousState) {
@@ -48,7 +48,6 @@
// set our scrim to black in this frame to avoid flickering and
// fade it out afterwards.
mBlankScreen = true;
- updateScrimColor(mScrimInFront, 1, Color.BLACK);
}
} else {
mAnimationDuration = ScrimController.ANIMATION_DURATION;
@@ -61,7 +60,7 @@
/**
* Showing password challenge.
*/
- BOUNCER {
+ BOUNCER(1) {
@Override
public void prepare(ScrimState previousState) {
mCurrentBehindAlpha = ScrimController.SCRIM_BEHIND_ALPHA_UNLOCKING;
@@ -72,7 +71,7 @@
/**
* Changing screen brightness from quick settings.
*/
- BRIGHTNESS_MIRROR {
+ BRIGHTNESS_MIRROR(2) {
@Override
public void prepare(ScrimState previousState) {
mCurrentBehindAlpha = 0;
@@ -83,12 +82,9 @@
/**
* Always on display or screen off.
*/
- AOD {
+ AOD(3) {
@Override
public void prepare(ScrimState previousState) {
- if (previousState == ScrimState.PULSING && !mCanControlScreenOff) {
- updateScrimColor(mScrimInFront, 1, Color.BLACK);
- }
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
final boolean wasPulsing = previousState == ScrimState.PULSING;
mBlankScreen = wasPulsing && !mCanControlScreenOff;
@@ -106,7 +102,7 @@
/**
* When phone wakes up because you received a notification.
*/
- PULSING {
+ PULSING(4) {
@Override
public void prepare(ScrimState previousState) {
mCurrentInFrontAlpha = 0;
@@ -115,16 +111,13 @@
&& !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
mCurrentBehindTint = Color.BLACK;
mBlankScreen = mDisplayRequiresBlanking;
- if (mDisplayRequiresBlanking) {
- updateScrimColor(mScrimInFront, 1, Color.BLACK);
- }
}
},
/**
* Unlocked on top of an app (launcher or any other activity.)
*/
- UNLOCKED {
+ UNLOCKED(5) {
@Override
public void prepare(ScrimState previousState) {
mCurrentBehindAlpha = 0;
@@ -164,6 +157,11 @@
boolean mCanControlScreenOff;
boolean mWallpaperSupportsAmbientMode;
KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ int mIndex;
+
+ ScrimState(int index) {
+ mIndex = index;
+ }
public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters) {
mScrimInFront = scrimInFront;
@@ -177,6 +175,10 @@
public void prepare(ScrimState previousState) {
}
+ public int getIndex() {
+ return mIndex;
+ }
+
public float getFrontAlpha() {
return mCurrentInFrontAlpha;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3777a6c..8c112a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -140,7 +140,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.AutoReinflateContainer;
-import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
@@ -152,6 +151,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -181,6 +181,7 @@
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -2460,14 +2461,25 @@
}
@Override
- public void showChargingAnimation(int batteryLevel) {
- if (mDozing) {
- // ambient
- } else if (mKeyguardManager.isKeyguardLocked()) {
- // lockscreen
- } else {
+ public void showWirelessChargingAnimation(int batteryLevel) {
+ if (mDozing || mKeyguardManager.isKeyguardLocked()) {
+ // on ambient or lockscreen, hide notification panel
WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- batteryLevel).show();
+ batteryLevel, new WirelessChargingAnimation.Callback() {
+ @Override
+ public void onAnimationStarting() {
+ CrossFadeHelper.fadeOut(mNotificationPanel, 1);
+ }
+
+ @Override
+ public void onAnimationEnded() {
+ CrossFadeHelper.fadeIn(mNotificationPanel);
+ }
+ }).show();
+ } else {
+ // workspace
+ WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
+ batteryLevel, null).show();
}
}
@@ -3883,6 +3895,7 @@
private void instantCollapseNotificationPanel() {
mNotificationPanel.instantCollapse();
+ runPostCollapseRunnables();
}
@Override
@@ -5044,6 +5057,8 @@
} else if (!isPresenterFullyCollapsed()) {
instantCollapseNotificationPanel();
visibilityChanged(false);
+ } else {
+ runPostCollapseRunnables();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 1897171..1da50ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -88,7 +88,8 @@
* Layout is happening from end -> start
*/
private void calculateIconTranslations() {
- float translationX = getWidth();
+ float width = getWidth();
+ float translationX = width;
float contentStart = getPaddingStart();
int childCount = getChildCount();
// Underflow === don't show content until that index
@@ -133,6 +134,15 @@
}
}
}
+
+ // Stole this from NotificationIconContainer. Not optimal but keeps the layout logic clean
+ if (isLayoutRtl()) {
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ ViewState state = getViewStateFromChild(child);
+ state.xTranslation = width - state.xTranslation - child.getWidth();
+ }
+ }
}
private void applyIconStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index a2b896d..040d7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -51,10 +51,11 @@
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
protected final Clock mClock = new Clock();
- protected final Context mContext;
protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
protected final Handler mHandler = new Handler(Looper.getMainLooper());
+ protected final Context mContext;
+
protected int mHeadsUpNotificationDecay;
protected int mMinimumDisplayTime;
protected int mTouchAcceptanceDelay;
@@ -64,7 +65,6 @@
private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
private final ArrayMap<String, Long> mSnoozedPackages;
- private final ContentObserver mSettingsObserver;
public HeadsUpManager(@NonNull final Context context) {
mContext = context;
@@ -78,7 +78,7 @@
mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
- mSettingsObserver = new ContentObserver(mHandler) {
+ ContentObserver settingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
final int packageSnoozeLengthMs = Settings.Global.getInt(
@@ -91,7 +91,7 @@
};
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
- mSettingsObserver);
+ settingsObserver);
}
/**
@@ -176,13 +176,12 @@
}
}
- protected void removeHeadsUpEntry(NotificationData.Entry entry) {
+ protected void removeHeadsUpEntry(@NonNull NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
onHeadsUpEntryRemoved(remove);
- releaseHeadsUpEntry(remove);
}
- protected void onHeadsUpEntryRemoved(HeadsUpEntry remove) {
+ protected void onHeadsUpEntryRemoved(@NonNull HeadsUpEntry remove) {
NotificationData.Entry entry = remove.entry;
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
@@ -190,6 +189,7 @@
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
+ releaseHeadsUpEntry(remove);
}
protected void updatePinnedMode() {
@@ -225,7 +225,7 @@
/**
* Returns if the given notification is in the Heads Up Notification list or not.
*/
- public boolean isHeadsUp(String key) {
+ public boolean isHeadsUp(@NonNull String key) {
return mHeadsUpEntries.containsKey(key);
}
@@ -282,10 +282,12 @@
}
}
+ @NonNull
private static String snoozeKey(@NonNull String packageName, int user) {
return user + "," + packageName;
}
+ @Nullable
protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
return mHeadsUpEntries.get(key);
}
@@ -295,6 +297,7 @@
*
* @param key Key of heads up notification
*/
+ @Nullable
public NotificationData.Entry getEntry(@NonNull String key) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
return entry != null ? entry.entry : null;
@@ -324,13 +327,14 @@
return !mHeadsUpEntries.isEmpty();
}
+ @Nullable
protected HeadsUpEntry getTopHeadsUpEntry() {
if (mHeadsUpEntries.isEmpty()) {
return null;
}
HeadsUpEntry topEntry = null;
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
- if (topEntry == null || entry.compareTo(topEntry) == -1) {
+ if (topEntry == null || entry.compareTo(topEntry) < 0) {
topEntry = entry;
}
}
@@ -344,12 +348,13 @@
mUser = user;
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("HeadsUpManager state:");
dumpInternal(fd, pw, args);
}
- protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dumpInternal(
+ @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
pw.print(" now="); pw.println(mClock.currentTimeMillis());
@@ -409,7 +414,7 @@
* @return -1 if the first argument should be ranked higher than the second, 1 if the second
* one should be ranked higher and 0 if they are equal.
*/
- public int compare(NotificationData.Entry a, NotificationData.Entry b) {
+ public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
if (aEntry == null || bEntry == null) {
@@ -419,11 +424,6 @@
}
/**
- * Sets an entry to be expanded and therefore stick in the heads up area if it's pinned
- * until it's collapsed again.
- */
-
- /**
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
@@ -440,7 +440,7 @@
}
protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
- // Do nothing for HeadsUpEntry.
+ entry.reset();
}
/**
@@ -454,7 +454,7 @@
public long earliestRemovaltime;
public boolean expanded;
- private Runnable mRemoveHeadsUpRunnable;
+ @Nullable private Runnable mRemoveHeadsUpRunnable;
public void setEntry(@Nullable final NotificationData.Entry entry) {
setEntry(entry, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 077c6c3..ea449c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -28,6 +28,8 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
@@ -45,6 +47,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
+import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
@@ -58,12 +61,16 @@
private long mDownTime;
private int mCode;
private int mTouchSlop;
+ private int mTouchDownX;
+ private int mTouchDownY;
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
private boolean mLongClicked;
private OnClickListener mOnClickListener;
private final KeyButtonRipple mRipple;
+ private final OverviewProxyService mOverviewProxyService;
+ private final Vibrator mVibrator;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Runnable mCheckLongPress = new Runnable() {
@@ -110,6 +117,8 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mRipple = new KeyButtonRipple(context, this);
+ mVibrator = mContext.getSystemService(Vibrator.class);
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
setBackground(mRipple);
}
@@ -189,6 +198,7 @@
}
public boolean onTouchEvent(MotionEvent ev) {
+ final boolean isProxyConnected = mOverviewProxyService.getProxy() != null;
final int action = ev.getAction();
int x, y;
if (action == MotionEvent.ACTION_DOWN) {
@@ -203,23 +213,34 @@
mDownTime = SystemClock.uptimeMillis();
mLongClicked = false;
setPressed(true);
+ mTouchDownX = (int) ev.getX();
+ mTouchDownY = (int) ev.getY();
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
- playSoundEffect(SoundEffectConstants.CLICK);
+ if (isProxyConnected) {
+ // Provide small vibration for quick step or immediate down feedback
+ AsyncTask.execute(() ->
+ mVibrator.vibrate(VibrationEffect
+ .get(VibrationEffect.EFFECT_TICK, false)));
+ } else {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
break;
case MotionEvent.ACTION_MOVE:
x = (int)ev.getX();
y = (int)ev.getY();
- setPressed(x >= -mTouchSlop
- && x < getWidth() + mTouchSlop
- && y >= -mTouchSlop
- && y < getHeight() + mTouchSlop);
+ boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > mTouchSlop;
+ boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > mTouchSlop;
+ if (exceededTouchSlopX || exceededTouchSlopY) {
+ setPressed(false);
+ removeCallbacks(mCheckLongPress);
+ }
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
@@ -231,9 +252,14 @@
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
- // Always send a release ourselves because it doesn't seem to be sent elsewhere
- // and it feels weird to sometimes get a release haptic and other times not.
- if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
+ if (isProxyConnected) {
+ if (doIt) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ } else if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
+ // Always send a release ourselves because it doesn't seem to be sent elsewhere
+ // and it feels weird to sometimes get a release haptic and other times not.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
if (mCode != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
index 5c888ac..6ed07f8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
@@ -49,6 +49,8 @@
import android.view.Window;
import android.view.WindowManager;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.Dependency;
@@ -81,6 +83,7 @@
private final MediaRouterWrapper mRouter;
private final MediaRouterCallback mRouterCallback;
private long mLastUpdateTime;
+ static final boolean INCLUDE_MEDIA_ROUTES = false;
private boolean mIsInCall;
protected boolean isAttached;
@@ -174,7 +177,7 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
- if (!mIsInCall) {
+ if (!mIsInCall && INCLUDE_MEDIA_ROUTES) {
mRouter.addCallback(mRouteSelector, mRouterCallback,
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
}
@@ -201,6 +204,7 @@
@Override
public void show() {
super.show();
+ Dependency.get(MetricsLogger.class).visible(MetricsProto.MetricsEvent.OUTPUT_CHOOSER);
mHardwareLayout.setTranslationX(getAnimTranslation());
mHardwareLayout.setAlpha(0);
mHardwareLayout.animate()
@@ -214,6 +218,7 @@
@Override
public void dismiss() {
+ Dependency.get(MetricsLogger.class).hidden(MetricsProto.MetricsEvent.OUTPUT_CHOOSER);
mHardwareLayout.setTranslationX(0);
mHardwareLayout.setAlpha(1);
mHardwareLayout.animate()
@@ -236,11 +241,15 @@
if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) {
+ Dependency.get(MetricsLogger.class).action(
+ MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_CONNECT);
mBluetoothController.connect(device);
}
} else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
if (route.isEnabled()) {
+ Dependency.get(MetricsLogger.class).action(
+ MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_CONNECT);
route.select();
}
}
@@ -251,8 +260,12 @@
if (item == null || item.tag == null) return;
if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
+ Dependency.get(MetricsLogger.class).action(
+ MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_DISCONNECT);
mBluetoothController.disconnect(device);
} else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
+ Dependency.get(MetricsLogger.class).action(
+ MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_DISCONNECT);
mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
}
}
@@ -272,7 +285,7 @@
addBluetoothDevices(items);
// Add remote displays
- if (!mIsInCall) {
+ if (!mIsInCall && INCLUDE_MEDIA_ROUTES) {
addRemoteDisplayRoutes(items);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index efa8386..0203c43 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -27,16 +27,15 @@
import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
@@ -53,8 +52,8 @@
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
- public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = true;
- public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = true;
+ public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
+ public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
private final SystemUI mSysui;
private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 9aee00e..3c29b77 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -362,12 +362,15 @@
private boolean shouldShowUI(int flags) {
updateStatusBar();
- return mStatusBar != null
- && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
- && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
+ // if status bar isn't null, check if phone is in AOD, else check flags
+ // since we could be using a different status bar
+ return mStatusBar != null ?
+ mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
+ && mStatusBar.getWakefulnessState() !=
+ WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
&& mStatusBar.isDeviceInteractive()
- && (flags & AudioManager.FLAG_SHOW_UI) != 0
- && mShowVolumeDialog;
+ && (flags & AudioManager.FLAG_SHOW_UI) != 0 && mShowVolumeDialog
+ : mShowVolumeDialog && (flags & AudioManager.FLAG_SHOW_UI) != 0;
}
boolean onVolumeChangedW(int stream, int flags) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0c6e0f6..c622677 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
@@ -47,6 +48,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.support.v7.media.MediaRouter;
+import android.text.InputFilter;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -219,7 +221,7 @@
R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false);
addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system,
R.drawable.ic_volume_system_mute, false, false);
- addRow(AudioManager.STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
+ addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
R.drawable.ic_volume_accessibility, true, false);
}
} else {
@@ -334,6 +336,9 @@
row.view.setTag(row);
row.header = row.view.findViewById(R.id.volume_row_header);
row.header.setId(20 * row.stream);
+ if (stream == STREAM_ACCESSIBILITY) {
+ row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
+ }
row.slider = row.view.findViewById(R.id.volume_row_slider);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
row.anim = null;
@@ -391,13 +396,9 @@
if (hasVibrator) {
mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
} else {
- final boolean wasZero = ss.level == 0;
- mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
}
} else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
- final boolean wasZero = ss.level == 0;
- mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
} else {
mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
@@ -643,7 +644,7 @@
if (ss.level == row.requestedLevel) {
row.requestedLevel = -1;
}
- final boolean isA11yStream = row.stream == AudioManager.STREAM_ACCESSIBILITY;
+ final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY;
final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
@@ -949,7 +950,7 @@
public void onAccessibilityModeChanged(Boolean showA11yStream) {
mShowA11yStream = showA11yStream == null ? false : showA11yStream;
VolumeRow activeRow = getActiveRow();
- if (!mShowA11yStream && AudioManager.STREAM_ACCESSIBILITY == activeRow.stream) {
+ if (!mShowA11yStream && STREAM_ACCESSIBILITY == activeRow.stream) {
dismissH(Events.DISMISS_STREAM_GONE);
} else {
updateRowsH(activeRow);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index f50a287..3d44381 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -14,26 +14,22 @@
package com.android.systemui.volume;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.Gravity;
+import android.view.DisplayCutout;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
import com.android.systemui.R;
import com.android.systemui.util.leak.RotationUtils;
@@ -46,6 +42,9 @@
private AnimatorSet mAnimation;
private boolean mHasOutsideTouch;
private int mRotation = ROTATION_NONE;
+ @Nullable
+ private DisplayCutout mDisplayCutout;
+
public VolumeUiLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -60,6 +59,7 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+ mDisplayCutout = null;
}
@Override
@@ -86,200 +86,64 @@
updateRotation();
}
+ private void setDisplayCutout() {
+ if (mDisplayCutout == null && getRootWindowInsets() != null) {
+ DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+ if (cutout != null) {
+ mDisplayCutout = cutout;
+ }
+ }
+ }
+
private void updateRotation() {
+ setDisplayCutout();
int rotation = RotationUtils.getRotation(getContext());
if (rotation != mRotation) {
- rotate(mRotation, rotation);
+ updateSafeInsets(rotation);
mRotation = rotation;
}
}
- private void rotate(View view, int from, int to, boolean swapDimens) {
- if (from != ROTATION_NONE && to != ROTATION_NONE) {
- // Rather than handling this confusing case, just do 2 rotations.
- rotate(view, from, ROTATION_NONE, swapDimens);
- rotate(view, ROTATION_NONE, to, swapDimens);
- return;
- }
- if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
- rotateRight(view);
- } else {
- rotateLeft(view);
- }
- if (to != ROTATION_NONE) {
- if (swapDimens && view instanceof LinearLayout) {
- LinearLayout linearLayout = (LinearLayout) view;
- linearLayout.setOrientation(LinearLayout.HORIZONTAL);
- swapDimens(view);
- }
- } else {
- if (swapDimens && view instanceof LinearLayout) {
- LinearLayout linearLayout = (LinearLayout) view;
- linearLayout.setOrientation(LinearLayout.VERTICAL);
- swapDimens(view);
- }
- }
- }
+ private void updateSafeInsets(int rotation) {
+ // Depending on our rotation, we may have to work around letterboxing from the right
+ // side from the navigation bar or a cutout.
- private void rotate(int from, int to) {
- View footer = mChild.findViewById(R.id.footer);
- rotate(footer, from, to, false);
- rotate(this, from, to, true);
- rotate(mChild, from, to, true);
- ViewGroup rows = mChild.findViewById(R.id.volume_dialog_rows);
- rotate(rows, from, to, true);
- swapOrientation((LinearLayout) rows);
- int rowCount = rows.getChildCount();
- for (int i = 0; i < rowCount; i++) {
- View row = rows.getChildAt(i);
- if (to == ROTATION_SEASCAPE) {
- rotateSeekBars(row, to, 180);
- } else if (to == ROTATION_LANDSCAPE) {
- rotateSeekBars(row, to, 0);
- } else {
- rotateSeekBars(row, to, 270);
- }
- rotate(row, from, to, true);
- }
- }
+ MarginLayoutParams lp = (MarginLayoutParams) mChild.getLayoutParams();
- private void swapOrientation(LinearLayout layout) {
- if(layout.getOrientation() == LinearLayout.HORIZONTAL) {
- layout.setOrientation(LinearLayout.VERTICAL);
- } else {
- layout.setOrientation(LinearLayout.HORIZONTAL);
- }
- }
-
- private void swapDimens(View v) {
- if (v == null) {
- return;
- }
- ViewGroup.LayoutParams params = v.getLayoutParams();
- int h = params.width;
- params.width = params.height;
- params.height = h;
- v.setLayoutParams(params);
- }
-
- private void rotateSeekBars(View row, int to, int rotation) {
- SeekBar seekbar = row.findViewById(R.id.volume_row_slider);
- if (seekbar != null) {
- seekbar.setRotation((float) rotation);
- }
-
- View parent = row.findViewById(R.id.volume_row_slider_frame);
- swapDimens(parent);
- ViewGroup.LayoutParams params = seekbar.getLayoutParams();
- ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
- if (to != ROTATION_NONE) {
- params.height = parentParams.height;
- params.width = parentParams.width;
- } else {
- params.height = parentParams.width;
- params.width = parentParams.height;
- }
- seekbar.setLayoutParams(params);
- }
-
- private int rotateGravityRight(int gravity) {
- int retGravity = 0;
- int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
- final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- retGravity |= Gravity.CENTER_VERTICAL;
+ int margin = (int) getResources().getDimension(R.dimen.volume_dialog_base_margin);
+ switch (rotation) {
+ /*
+ * Landscape: <-|. Have to deal with the nav bar
+ * Seascape: |->. Have to deal with the cutout
+ */
+ case RotationUtils.ROTATION_LANDSCAPE:
+ margin += getNavBarHeight();
break;
- case Gravity.RIGHT:
- retGravity |= Gravity.BOTTOM;
+ case RotationUtils.ROTATION_SEASCAPE:
+ margin += getDisplayCutoutHeight();
break;
- case Gravity.LEFT:
default:
- retGravity |= Gravity.TOP;
break;
}
- switch (verticalGravity) {
- case Gravity.CENTER_VERTICAL:
- retGravity |= Gravity.CENTER_HORIZONTAL;
- break;
- case Gravity.BOTTOM:
- retGravity |= Gravity.LEFT;
- break;
- case Gravity.TOP:
- default:
- retGravity |= Gravity.RIGHT;
- break;
- }
- return retGravity;
+ lp.rightMargin = margin;
+ mChild.setLayoutParams(lp);
}
- private int rotateGravityLeft(int gravity) {
- if (gravity == -1) {
- gravity = Gravity.TOP | Gravity.START;
- }
- int retGravity = 0;
- int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
- final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- retGravity |= Gravity.CENTER_VERTICAL;
- break;
- case Gravity.RIGHT:
- retGravity |= Gravity.TOP;
- break;
- case Gravity.LEFT:
- default:
- retGravity |= Gravity.BOTTOM;
- break;
- }
-
- switch (verticalGravity) {
- case Gravity.CENTER_VERTICAL:
- retGravity |= Gravity.CENTER_HORIZONTAL;
- break;
- case Gravity.BOTTOM:
- retGravity |= Gravity.RIGHT;
- break;
- case Gravity.TOP:
- default:
- retGravity |= Gravity.LEFT;
- break;
- }
- return retGravity;
+ private int getNavBarHeight() {
+ return (int) getResources().getDimension(R.dimen.navigation_bar_size);
}
- private void rotateLeft(View v) {
- if (v.getParent() instanceof FrameLayout) {
- LayoutParams p = (LayoutParams) v.getLayoutParams();
- p.gravity = rotateGravityLeft(p.gravity);
+ //TODO: Find a better way
+ private int getDisplayCutoutHeight() {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+ return 0;
}
- v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(),
- v.getPaddingLeft());
- MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
- params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin,
- params.leftMargin);
- v.setLayoutParams(params);
+ Rect r = mDisplayCutout.getBoundingRect();
+ return r.bottom - r.top;
}
- private void rotateRight(View v) {
- if (v.getParent() instanceof FrameLayout) {
- LayoutParams p = (LayoutParams) v.getLayoutParams();
- p.gravity = rotateGravityRight(p.gravity);
- }
-
- v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(),
- v.getPaddingRight());
- MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
- params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin,
- params.rightMargin);
- v.setLayoutParams(params);
- }
private void animateChild(int oldHeight, int newHeight) {
if (true) return;
@@ -309,12 +173,6 @@
return super.getOutlineProvider();
}
- @Override
- public void setPressed(boolean pressed)
- {
- // Ignore presses because it activates the seekbar thumb unnecessarily.
- }
-
public void setOutsideTouchListener(OnClickListener onClickListener) {
mHasOutsideTouch = true;
requestLayout();
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 936ff51..59a7da6 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -65,7 +65,6 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
telephony-common \
- android.car \
android.test.base \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 3e37cfe..bf6cc53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -151,22 +151,4 @@
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
}
-
- @Test
- public void testGetTimeRemainingFormatted_roundsDownTo15() {
- mPowerNotificationWarnings.updateEstimate(
- new Estimate(TimeUnit.MINUTES.toMillis(57), true));
- String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
- assertTrue("time:" + time + ", expected: " + FORMATTED_45M, time.equals(FORMATTED_45M));
- }
-
- @Test
- public void testGetTimeRemainingFormatted_keepsMinutesWhenZero() {
- mPowerNotificationWarnings.updateEstimate(
- new Estimate(TimeUnit.MINUTES.toMillis(65), true));
- String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
- assertTrue("time:" + time + ", expected: " + FORMATTED_HOUR, time.equals(FORMATTED_HOUR));
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 5f763a4..1204dda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -86,13 +86,13 @@
mHeadsUpManager);
gutsManager.setUpWithPresenter(mPresenter, entryManager, mListContainer,
mCheckSaveListener, mOnClickListener);
- notificationListener.setUpWithPresenter(mPresenter, entryManager);
notificationLogger.setUpWithEntryManager(entryManager, mListContainer);
mediaManager.setUpWithPresenter(mPresenter, entryManager);
remoteInputManager.setUpWithPresenter(mPresenter, entryManager, mRemoteInputManagerCallback,
mDelegate);
lockscreenUserManager.setUpWithPresenter(mPresenter, entryManager);
viewHierarchyManager.setUpWithPresenter(mPresenter, entryManager, mListContainer);
+ notificationListener.setUpWithPresenter(mPresenter, entryManager);
assertFalse(mDependency.hasInstantiatedDependency(StatusBarWindowManager.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 28f9417..aa991cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -17,13 +17,12 @@
package com.android.systemui.statusbar.phone;
import android.app.ActivityManager;
+import android.app.Instrumentation;
import android.app.Notification;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.view.View;
import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -43,6 +42,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertFalse;
@@ -65,8 +65,6 @@
private NotificationData.Entry mEntry;
private StatusBarNotification mSbn;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
@Mock private NotificationGroupManager mGroupManager;
@Mock private View mStatusBarWindowView;
@Mock private StatusBar mBar;
@@ -77,7 +75,8 @@
public void setUp() {
when(mVSManager.isReorderingAllowed()).thenReturn(true);
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
+ mHeadsUpManager = new HeadsUpManagerPhone(
+ mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
Notification.Builder n = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
@@ -115,12 +114,103 @@
assertEquals(1, mHeadsUpManager.getAllEntries().count());
assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
- // Remove but defer, since the notification is visible on display.
- mHeadsUpManager.removeNotification(mEntry.key, false);
+ // Try to remove but defer, since the notification is currenlt visible on display.
+ mHeadsUpManager.removeNotification(mEntry.key, false /* ignoreEarliestRemovalTime */);
assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
assertEquals(mEntry, mHeadsUpManager.getTopEntry());
assertEquals(1, mHeadsUpManager.getAllEntries().count());
assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove forcibly with ignoreEarliestRemovalTime = true.
+ mHeadsUpManager.removeNotification(mEntry.key, true /* ignoreEarliestRemovalTime */);
+
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void testsTimeoutRemoval() {
+ mHeadsUpManager.removeMinimumDisplayTimeForTesting();
+
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // Run the code on the main thready, not to run an async operations.
+ instrumentation.runOnMainSync(() -> {
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ // Ensure the head up is visible before timeout.
+ assertNotNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNotNull(mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+ });
+ // Wait for the async operations, which removes the heads up notification.
+ waitForIdleSync();
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void releaseImmediately() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove but defer, since the notification is visible on display.
+ mHeadsUpManager.releaseImmediately(mEntry.key);
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void releaseAllImmediately() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove but defer, since the notification is visible on display.
+ mHeadsUpManager.releaseAllImmediately();
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 6d2691c..43e16db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -38,6 +38,7 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Choreographer;
import android.view.View;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -374,7 +375,6 @@
onPreDraw();
// Force finish screen blanking.
- endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK);
mHandler.dispatchQueuedMessages();
// Force finish all animations.
endAnimation(mScrimBehind, TAG_KEY_ANIM);
@@ -401,6 +401,15 @@
protected WakeLock createWakeLock() {
return mWakeLock;
}
+
+ /**
+ * Do not wait for a frame since we're in a test environment.
+ * @param callback What to execute.
+ */
+ @Override
+ protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
+ callback.doFrame(0);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index a10bebf..e1b97bda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -29,12 +29,14 @@
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
+@Ignore
public class LocationControllerImplTest extends SysuiTestCase {
private LocationControllerImpl mLocationController;
@@ -79,4 +81,4 @@
TestableLooper.get(this).processAllMessages();
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
index c18ed73..922bde7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.volume;
+import static com.android.systemui.volume.OutputChooserDialog.INCLUDE_MEDIA_ROUTES;
+
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -44,11 +46,13 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@Ignore
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -87,7 +91,7 @@
public void tearDown() throws Exception {
TestableLooper.get(this).processAllMessages();
}
-
+/*
@Test
public void testClickMediaRouterItemConnectsMedia() {
mDialog.show();
@@ -137,7 +141,7 @@
.getText().toString().contains("Phone"));
mDialog.dismiss();
}
-
+*/
@Test
public void testNoMediaScanIfInCall() {
mDialog.setIsInCall(true);
@@ -147,19 +151,13 @@
}
@Test
- public void testMediaScanIfNotInCall() {
- mDialog.setIsInCall(false);
- mDialog.onAttachedToWindow();
-
- verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
- }
-
- @Test
public void testRegisterCallbacks() {
mDialog.setIsInCall(false);
mDialog.onAttachedToWindow();
- verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
+ if (INCLUDE_MEDIA_ROUTES) {
+ verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
+ }
verify(mController, times(1)).addCallback(any());
verify(mVolumeController, times(1)).addCallback(any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 401fd68..a4ed459 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -76,6 +76,16 @@
}
@Test
+ public void testVolumeChangeW_nullStatusBar() {
+ VolumeDialogControllerImpl.C callback = mock(VolumeDialogControllerImpl.C.class);
+ TestableVolumeDialogControllerImpl nullStatusBarTestableDialog = new
+ TestableVolumeDialogControllerImpl(mContext, callback, null);
+ nullStatusBarTestableDialog.setEnableDialogs(true, true);
+ nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+ verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+ }
+
+ @Test
public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
MediaSession.Token token = new MediaSession.Token(null);
mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 4888fb2..43d60e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -55,6 +56,7 @@
import java.util.function.Predicate;
+@Ignore
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -111,7 +113,7 @@
+ " failed test", condition.test(view));
}
}
-
+/*
@Test
public void testContentDescriptions() {
mDialog.show(SHOW_REASON_UNKNOWN);
@@ -218,4 +220,5 @@
verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
}
+ */
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
deleted file mode 100644
index 4ab2063..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
+++ /dev/null
@@ -1,220 +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 com.android.systemui.volume;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.net.Uri;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.FlakyTest;
-import android.view.LayoutInflater;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Ignore
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ZenModePanelTest extends SysuiTestCase {
-
- ZenModePanel mPanel;
- ZenModeController mController;
- Uri mForeverId;
-
- @Before
- public void setup() throws Exception {
- final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- mPanel = (ZenModePanel) layoutInflater.inflate(com.android.systemui.R.layout.zen_mode_panel,
- null);
- mController = mock(ZenModeController.class);
- mForeverId = Condition.newId(mContext).appendPath("forever").build();
-
- mPanel.init(mController);
- }
-
- private void assertProperConditionTagTypes(boolean hasAlarm) {
- final int N = mPanel.getVisibleConditions();
- assertEquals(hasAlarm ? 3 : 2, N);
-
- assertEquals(mForeverId, mPanel.getConditionTagAt(0).condition.id);
- assertTrue(ZenModeConfig.isValidCountdownConditionId(
- mPanel.getConditionTagAt(1).condition.id));
- assertFalse(ZenModeConfig.isValidCountdownToAlarmConditionId(
- mPanel.getConditionTagAt(1).condition.id));
- if (hasAlarm) {
- assertTrue(ZenModeConfig.isValidCountdownToAlarmConditionId(
- mPanel.getConditionTagAt(2).condition.id));
- }
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_alarmExists() {
- Condition forever = new Condition(mForeverId, "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(forever);
- assertProperConditionTagTypes(true);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_noAlarm() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(forever);
- assertProperConditionTagTypes(false);
- assertEquals(foreverId, mPanel.getConditionTagAt(0).condition.id);
- }
-
- @Test
- public void testHandleUpdateConditions_countdownSelected_alarmExists() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(countdown);
- assertProperConditionTagTypes(true);
- assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_countdownSelected_noAlarm() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(countdown);
- assertProperConditionTagTypes(false);
- assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_nextAlarmSelected() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 1000, true),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 9000);
-
- mPanel.handleUpdateConditions(alarm);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_alarmConditionDoesNotChangeIfAttached() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 9000, true),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(alarm);
- mPanel.setAttached(true);
- mPanel.handleUpdateConditions(forever);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_timeConditionDoesNotChangeIfAttached() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(countdown);
- mPanel.setAttached(true);
- mPanel.handleUpdateConditions(forever);
-
- assertProperConditionTagTypes(false);
- assertEquals(countdown, mPanel.getConditionTagAt(1).condition);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- @UiThreadTest
- public void testHandleUpdateManualRule_stillSelectedAfterModeChange() {
- ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 1000, true),
- "", Condition.STATE_TRUE);
-
- rule.condition = alarm;
- rule.conditionId = alarm.id;
- rule.enabled = true;
- rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-
- mPanel.handleUpdateManualRule(rule);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
- assertEquals(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
-
- rule.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
-
- mPanel.handleUpdateManualRule(rule);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
- assertEquals(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS,
- mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
- }
-}
diff --git a/proto/Android.bp b/proto/Android.bp
index 95f453c..f3811bd 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -6,6 +6,8 @@
},
srcs: ["src/**/*.proto"],
no_framework_libs: true,
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
target: {
android: {
jarjar_rules: "jarjar-rules.txt",
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 01714cf..ae5e133 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5171,6 +5171,7 @@
// An autofill service was bound using an unofficial(but still supported) permission.
// Package: Package of the autofill service
// OS: P
+
AUTOFILL_INVALID_PERMISSION = 1289;
// OPEN: QS Alarm tile shown
@@ -5185,6 +5186,33 @@
// OS: P
USB_DEVICE_DETAILS = 1291;
+ // OPEN: Settings > Accessibility > Vibration
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACCESSIBILITY_VIBRATION = 1292;
+
+ // OPEN: Settings > Accessibility > Vibration > Ring & notification vibration
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACCESSIBILITY_VIBRATION_NOTIFICATION = 1293;
+
+ // OPEN: Settings > Accessibility > Vibration > Touch vibration
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACCESSIBILITY_VIBRATION_TOUCH = 1294;
+
+ // OPEN: Volume panel > output chooser dialog
+ // OS: P
+ OUTPUT_CHOOSER = 1295;
+
+ // Action: Volume panel > output chooser dialog > tap on device
+ // OS: P
+ ACTION_OUTPUT_CHOOSER_CONNECT = 1296;
+
+ // Action: Volume panel > output chooser dialog > tap on X next to connected device
+ // OS: P
+ ACTION_OUTPUT_CHOOSER_DISCONNECT = 1297;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index f6a54af..c77dcc0 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -63,7 +63,7 @@
// Number scans that returned at least one result.
optional int32 num_non_empty_scan_results = 13;
- // Number of scans that were one time.
+ // Number of single scans requests.
optional int32 num_oneshot_scans = 14;
// Number of repeated background scans that were scheduled to the chip.
@@ -376,6 +376,9 @@
// Wifi power statistics
optional WifiPowerStats wifi_power_stats = 92;
+
+ // Number of connectivity single scan requests.
+ optional int32 num_connectivity_oneshot_scans = 93;
}
// Information that gets logged for every WiFi connection.
@@ -1155,4 +1158,4 @@
// Amount of time wifi is in tx (ms)
optional int64 tx_time_ms = 5;
-}
\ No newline at end of file
+}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b32be73..52d0e08e 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -24,8 +24,9 @@
#include <utils/misc.h>
#include <inttypes.h>
+#include <android-base/macros.h>
#include <androidfw/Asset.h>
-#include <androidfw/AssetManager.h>
+#include <androidfw/AssetManager2.h>
#include <androidfw/ResourceTypes.h>
#include <android-base/macros.h>
@@ -1664,18 +1665,22 @@
static jlong
nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path)
{
- AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
+ }
}
- jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset);
+ jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release());
return id;
}
@@ -1752,22 +1757,25 @@
nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path,
jfloat fontSize, jint dpi)
{
- AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
+ }
}
jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con,
str.c_str(), str.length(),
fontSize, dpi,
asset->getBuffer(false), asset->getLength());
- delete asset;
return id;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index a70b88e..c36bb6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -901,6 +901,7 @@
}
}
+ @GuardedBy("mLock")
private void setMagnificationSpecLocked(MagnificationSpec spec) {
if (mEnabled) {
if (DEBUG_SET_MAGNIFICATION_SPEC) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillCompatAccessibilityService.java b/services/autofill/java/com/android/server/autofill/AutofillCompatAccessibilityService.java
new file mode 100644
index 0000000..075fd6b
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillCompatAccessibilityService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.autofill;
+
+import android.accessibilityservice.AccessibilityService;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Pseudo accessibility service reported to apps in autofill compatibility mode.
+ */
+public final class AutofillCompatAccessibilityService extends AccessibilityService {
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onInterrupt() {
+ /* do nothing */
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 2b73b33..d5a722b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -24,8 +24,10 @@
import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
@@ -304,6 +306,7 @@
*
* @return service instance.
*/
+ @GuardedBy("mLock")
@NonNull
AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -323,6 +326,7 @@
*
* @return service instance or {@code null} if not already present
*/
+ @GuardedBy("mLock")
@Nullable
AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -472,6 +476,7 @@
/**
* Removes a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void removeCachedServiceLocked(int userId) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
@@ -483,6 +488,7 @@
/**
* Updates a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void updateCachedServiceLocked(int userId) {
updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
}
@@ -490,6 +496,7 @@
/**
* Updates a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void updateCachedServiceLocked(int userId, boolean disabled) {
AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
@@ -508,6 +515,18 @@
if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
mUi.hideAll(null);
}
+
+ @Override
+ public boolean isCompatibilityModeRequested(@NonNull String packageName,
+ long versionCode, @UserIdInt int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ return service.isCompatibilityModeRequestedLocked(packageName, versionCode);
+ }
+ }
+ return false;
+ }
}
final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index eb031f3..2dcc6da 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -40,6 +40,7 @@
import android.metrics.LogMaker;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
@@ -82,6 +83,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Random;
/**
@@ -209,6 +211,7 @@
}
}
+ @GuardedBy("mLock")
private int getServiceUidLocked() {
if (mInfo == null) {
Slog.w(TAG, "getServiceUidLocked(): no mInfo");
@@ -246,6 +249,7 @@
mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
}
+ @GuardedBy("mLock")
void updateLocked(boolean disabled) {
final boolean wasEnabled = isEnabledLocked();
if (sVerbose) {
@@ -273,8 +277,7 @@
}
try {
if (serviceInfo != null) {
- mInfo = new AutofillServiceInfo(mContext.getPackageManager(),
- serviceComponent, mUserId);
+ mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId);
if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
} else {
mInfo = null;
@@ -299,6 +302,7 @@
}
}
+ @GuardedBy("mLock")
boolean addClientLocked(IAutoFillManagerClient client) {
if (mClients == null) {
mClients = new RemoteCallbackList<>();
@@ -307,12 +311,14 @@
return isEnabledLocked();
}
+ @GuardedBy("mLock")
void removeClientLocked(IAutoFillManagerClient client) {
if (mClients != null) {
mClients.unregister(client);
}
}
+ @GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -335,6 +341,7 @@
}
}
+ @GuardedBy("mLock")
int startSessionLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
@@ -388,6 +395,7 @@
/**
* Remove abandoned sessions if needed.
*/
+ @GuardedBy("mLock")
private void pruneAbandonedSessionsLocked() {
long now = System.currentTimeMillis();
if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
@@ -399,6 +407,7 @@
}
}
+ @GuardedBy("mLock")
void finishSessionLocked(int sessionId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -422,6 +431,7 @@
}
}
+ @GuardedBy("mLock")
void cancelSessionLocked(int sessionId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -435,6 +445,7 @@
session.removeSelfLocked();
}
+ @GuardedBy("mLock")
void disableOwnedAutofillServicesLocked(int uid) {
Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
if (mInfo == null) return;
@@ -467,6 +478,7 @@
}
}
+ @GuardedBy("mLock")
private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, boolean hasCallback,
@NonNull ComponentName componentName, int flags) {
@@ -545,6 +557,7 @@
/**
* Updates a session and returns whether it should be restarted.
*/
+ @GuardedBy("mLock")
boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
AutofillValue value, int action, int flags) {
final Session session = mSessions.get(sessionId);
@@ -567,6 +580,7 @@
return false;
}
+ @GuardedBy("mLock")
void removeSessionLocked(int sessionId) {
mSessions.remove(sessionId);
}
@@ -602,6 +616,7 @@
}
}
+ @GuardedBy("mLock")
void destroyLocked() {
if (sVerbose) Slog.v(TAG, "destroyLocked()");
@@ -654,6 +669,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isValidEventLocked(String method, int sessionId) {
if (mEventHistory == null) {
Slog.w(TAG, method + ": not logging event because history is null");
@@ -725,6 +741,7 @@
/**
* Updates the last fill response when an autofill context is committed.
*/
+ @GuardedBy("mLock")
void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
@Nullable ArrayList<String> selectedDatasets,
@Nullable ArraySet<String> ignoredDatasets,
@@ -738,6 +755,7 @@
manuallyFilledDatasetIds, null, null, appPackageName);
}
+ @GuardedBy("mLock")
void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
@Nullable ArrayList<String> selectedDatasets,
@Nullable ArraySet<String> ignoredDatasets,
@@ -846,6 +864,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isCalledByServiceLocked(String methodName, int callingUid) {
if (getServiceUidLocked() != callingUid) {
Slog.w(TAG, methodName + "() called by UID " + callingUid
@@ -855,13 +874,19 @@
return true;
}
+ @GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
- pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
- ? mInfo.getServiceInfo().getComponentName() : null);
+ pw.print(prefix); pw.print("Autofill Service Info: ");
+ if (mInfo == null) {
+ pw.println("N/A");
+ } else {
+ pw.println();
+ mInfo.dump(prefix2, pw);
+ }
pw.print(prefix); pw.print("Component from settings: ");
pw.println(getComponentNameFromSettings());
pw.print(prefix); pw.print("Default component: ");
@@ -869,6 +894,7 @@
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Field classification enabled: ");
pw.println(isFieldClassificationEnabledLocked());
+ pw.print(prefix); pw.print("Compat pkgs: "); pw.println(getWhitelistedCompatModePackages());
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
@@ -958,6 +984,7 @@
mFieldClassificationStrategy.dump(prefix2, pw);
}
+ @GuardedBy("mLock")
void destroySessionsLocked() {
if (mSessions.size() == 0) {
mUi.destroyAll(null, null, false);
@@ -969,6 +996,7 @@
}
// TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
+ @GuardedBy("mLock")
void destroyFinishedSessionsLocked() {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
@@ -980,6 +1008,7 @@
}
}
+ @GuardedBy("mLock")
void listSessionsLocked(ArrayList<String> output) {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
@@ -988,6 +1017,26 @@
}
}
+ @GuardedBy("mLock")
+ boolean isCompatibilityModeRequestedLocked(@NonNull String packageName,
+ long versionCode) {
+ if (mInfo == null || !mInfo.isCompatibilityModeRequested(packageName, versionCode)) {
+ return false;
+ }
+ if (!Build.IS_ENG) {
+ // TODO: Build a map and watch for settings changes (this is called on app start)
+ final String whiteListedPackages = getWhitelistedCompatModePackages();
+ return whiteListedPackages != null && whiteListedPackages.contains(packageName);
+ }
+ return true;
+ }
+
+ private String getWhitelistedCompatModePackages() {
+ return Settings.Global.getString(
+ mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+ }
+
private void sendStateToClients(boolean resetClient) {
final RemoteCallbackList<IAutoFillManagerClient> clients;
final int userClientCount;
@@ -1034,6 +1083,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
final int sessionCount = mSessions.size();
for (int i = 0; i < sessionCount; i++) {
@@ -1045,6 +1095,7 @@
return true;
}
+ @GuardedBy("mLock")
boolean isEnabledLocked() {
return mSetupComplete && mInfo != null && !mDisabled;
}
@@ -1097,6 +1148,7 @@
/**
* Checks if autofill is disabled by service to the given activity.
*/
+ @GuardedBy("mLock")
private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
// Check activities first.
long elapsedTime = 0;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index aea9ad0..fe6d4c4 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -475,6 +475,7 @@
return true;
}
+ @GuardedBy("mLock")
protected boolean isCancelledLocked() {
return mCancelled;
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6108afa..4a24704 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -300,6 +300,7 @@
/**
* Returns the ids of all entries in {@link #mViewStates} in the same order.
*/
+ @GuardedBy("mLock")
private AutofillId[] getIdsOfAllViewStatesLocked() {
final int numViewState = mViewStates.size();
final AutofillId[] ids = new AutofillId[numViewState];
@@ -346,6 +347,7 @@
* <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts},
* or {@code null} when not found on either of them.
*/
+ @GuardedBy("mLock")
private AutofillValue findValueLocked(@NonNull AutofillId id) {
final ViewState state = mViewStates.get(id);
if (state == null) {
@@ -369,6 +371,7 @@
* @param fillContext The context to be filled
* @param flags The flags that started the session
*/
+ @GuardedBy("mLock")
private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) {
final ViewNode[] nodes = fillContext
.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
@@ -409,6 +412,7 @@
/**
* Cancels the last request sent to the {@link #mRemoteFillService}.
*/
+ @GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
@@ -430,6 +434,7 @@
/**
* Reads a new structure and then request a new fill response from the fill service.
*/
+ @GuardedBy("mLock")
private void requestNewFillResponseLocked(int flags) {
int requestId;
@@ -497,6 +502,7 @@
*
* @return The activity token
*/
+ @GuardedBy("mLock")
@NonNull IBinder getActivityTokenLocked() {
return mActivityToken;
}
@@ -663,6 +669,7 @@
*
* @return The context or {@code null} if there is no context
*/
+ @GuardedBy("mLock")
@Nullable private FillContext getFillContextByRequestIdLocked(int requestId) {
if (mContexts == null) {
return null;
@@ -820,6 +827,7 @@
});
}
+ @GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int authenticationId) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "
@@ -882,6 +890,7 @@
}
}
+ @GuardedBy("mLock")
void setHasCallbackLocked(boolean hasIt) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
@@ -891,6 +900,7 @@
mHasCallback = hasIt;
}
+ @GuardedBy("mLock")
@Nullable
private FillResponse getLastResponseLocked(@Nullable String logPrefix) {
if (mContexts == null) {
@@ -923,6 +933,7 @@
return response;
}
+ @GuardedBy("mLock")
@Nullable
private SaveInfo getSaveInfoLocked() {
final FillResponse response = getLastResponseLocked(null);
@@ -941,13 +952,14 @@
});
}
+ @GuardedBy("mLock")
private void logContextCommittedLocked() {
final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
if (lastResponse == null) return;
final int flags = lastResponse.getFlags();
if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
- if (sDebug) Slog.d(TAG, "logContextCommittedLocked(): ignored by flags " + flags);
+ if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): ignored by flags " + flags);
return;
}
@@ -1241,6 +1253,7 @@
*
* @return {@code true} if session is done, or {@code false} if it's pending user action.
*/
+ @GuardedBy("mLock")
public boolean showSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
@@ -1431,7 +1444,10 @@
}
}
- if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!");
+ if (sDebug) {
+ Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
+ + id + "!");
+ }
// Use handler so logContextCommitted() is logged first
mHandlerCaller.getHandler().post(() -> mService.logSaveShown(id, mClientState));
@@ -1455,7 +1471,7 @@
}
// Nothing changed...
if (sDebug) {
- Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities."
+ Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities."
+ "allRequiredAreNotNull=" + allRequiredAreNotEmpty
+ ", atLeastOneChanged=" + atLeastOneChanged);
}
@@ -1507,6 +1523,7 @@
/**
* Returns whether the session is currently showing the save UI
*/
+ @GuardedBy("mLock")
boolean isSavingLocked() {
return mIsSaving;
}
@@ -1514,6 +1531,7 @@
/**
* Gets the latest non-empty value for the given id in the autofill contexts.
*/
+ @GuardedBy("mLock")
@Nullable
private AutofillValue getValueFromContextsLocked(AutofillId id) {
final int numContexts = mContexts.size();
@@ -1536,6 +1554,7 @@
/**
* Gets the latest autofill options for the given id in the autofill contexts.
*/
+ @GuardedBy("mLock")
@Nullable
private CharSequence[] getAutofillOptionsFromContextsLocked(AutofillId id) {
final int numContexts = mContexts.size();
@@ -1553,6 +1572,7 @@
/**
* Calls service when user requested save.
*/
+ @GuardedBy("mLock")
void callSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
@@ -1643,6 +1663,7 @@
* @param viewState The view that is entered.
* @param flags The flag that was passed by the AutofillManager.
*/
+ @GuardedBy("mLock")
private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
@@ -1670,6 +1691,7 @@
*
* @return {@code true} iff a new partition should be started
*/
+ @GuardedBy("mLock")
private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
if (mResponses == null) {
return true;
@@ -1718,6 +1740,7 @@
return true;
}
+ @GuardedBy("mLock")
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
int flags) {
if (mDestroyed) {
@@ -1827,6 +1850,7 @@
/**
* Checks whether a view should be ignored.
*/
+ @GuardedBy("mLock")
private boolean isIgnoredLocked(AutofillId id) {
// Always check the latest response only
final FillResponse response = getLastResponseLocked(null);
@@ -1907,6 +1931,7 @@
}
}
+ @GuardedBy("mLock")
private void updateTrackedIdsLocked() {
// Only track the views of the last response as only those are reported back to the
// service, see #showSaveLocked
@@ -1970,7 +1995,7 @@
try {
if (sVerbose) {
Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
- + " (triggering on " + saveTriggerId + ")");
+ + " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish);
}
mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
saveOnFinish, toArray(fillableIds), saveTriggerId);
@@ -1979,6 +2004,7 @@
}
}
+ @GuardedBy("mLock")
private void replaceResponseLocked(@NonNull FillResponse oldResponse,
@NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
// Disassociate view states with the old response
@@ -2002,6 +2028,7 @@
removeSelf();
}
+ @GuardedBy("mLock")
private void processResponseLocked(@NonNull FillResponse newResponse,
@Nullable Bundle newClientState, int flags) {
// Make sure we are hiding the UI which will be shown
@@ -2039,6 +2066,7 @@
/**
* Sets the state of all views in the given response.
*/
+ @GuardedBy("mLock")
private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse) {
final List<Dataset> datasets = response.getDatasets();
if (datasets != null) {
@@ -2087,6 +2115,7 @@
/**
* Sets the state of all views in the given dataset and response.
*/
+ @GuardedBy("mLock")
private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset,
int state, boolean clearResponse) {
final ArrayList<AutofillId> ids = dataset.getFieldIds();
@@ -2107,6 +2136,7 @@
}
}
+ @GuardedBy("mLock")
private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state,
@Nullable AutofillValue value) {
ViewState viewState = mViewStates.get(id);
@@ -2168,6 +2198,7 @@
}
// TODO: this should never be null, but we got at least one occurrence, probably due to a race.
+ @GuardedBy("mLock")
@Nullable
private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
final Intent fillInIntent = new Intent();
@@ -2200,6 +2231,7 @@
return "Session: [id=" + id + ", component=" + mComponentName + "]";
}
+ @GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("id: "); pw.println(id);
@@ -2329,6 +2361,7 @@
* disabled it).
* </ul>
*/
+ @GuardedBy("mLock")
RemoteFillService destroyLocked() {
if (mDestroyed) {
return null;
@@ -2344,6 +2377,7 @@
* Cleans up this session and remove it from the service always, even if it does have a pending
* Save UI.
*/
+ @GuardedBy("mLock")
void forceRemoveSelfLocked() {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
@@ -2373,6 +2407,7 @@
* Cleans up this session and remove it from the service, but but only if it does not have a
* pending Save UI.
*/
+ @GuardedBy("mLock")
void removeSelfLocked() {
if (sVerbose) Slog.v(TAG, "removeSelfLocked(): " + mPendingSaveUi);
if (mDestroyed) {
@@ -2401,6 +2436,7 @@
* a specific {@code token} created by
* {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
*/
+ @GuardedBy("mLock")
boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
}
@@ -2408,10 +2444,12 @@
/**
* Checks whether this session is hiding the Save UI to handle a custom description link.
*/
+ @GuardedBy("mLock")
private boolean isSaveUiPendingLocked() {
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
}
+ @GuardedBy("mLock")
private int getLastResponseIndexLocked() {
// The response ids are monotonically increasing so
// we just find the largest id which is the last. We
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 5f112c7..8240e4b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -513,6 +513,12 @@
public void show(WindowManager.LayoutParams params) {
if (sVerbose) Slog.v(TAG, "show(): showing=" + mShowing + ", params="+ params);
try {
+ // Okay here is a bit of voodoo - we want to show the window as system
+ // controlled one so it covers app windows - adjust the params accordingly.
+ params.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ params.token = null;
+ params.packageName = "android";
+ params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
if (!mShowing) {
params.accessibilityTitle = mContentView.getContext()
.getString(R.string.autofill_picker_accessibility_title);
@@ -565,7 +571,6 @@
}
return false;
}
-
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3b80f55..83367f3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1931,6 +1931,7 @@
/**
* Remove a package from the full-data queue.
*/
+ @GuardedBy("mQueueLock")
private void dequeueFullBackupLocked(String packageName) {
final int N = mFullBackupQueue.size();
for (int i = N - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 30dfee8..7ab5812 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -58,6 +58,7 @@
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Pair;
@@ -79,7 +80,6 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Random;
@@ -88,6 +88,7 @@
import java.util.function.Predicate;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -224,10 +225,12 @@
interface Stats {
int REBATCH_ALL_ALARMS = 0;
+ int REORDER_ALARMS_FOR_STANDBY = 1;
}
private final StatLogger mStatLogger = new StatLogger(new String[] {
"REBATCH_ALL_ALARMS",
+ "REORDER_ALARMS_FOR_STANDBY",
});
/**
@@ -522,6 +525,10 @@
return newStart;
}
+ boolean remove(Alarm alarm) {
+ return remove(a -> (a == alarm));
+ }
+
boolean remove(Predicate<Alarm> predicate) {
boolean didRemove = false;
long newStart = 0; // recalculate endpoints as we go
@@ -741,6 +748,23 @@
return (index == 0);
}
+ private void insertAndBatchAlarmLocked(Alarm alarm) {
+ final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
+ : attemptCoalesceLocked(alarm.whenElapsed, alarm.maxWhenElapsed);
+
+ if (whichBatch < 0) {
+ addBatchLocked(mAlarmBatches, new Batch(alarm));
+ } else {
+ final Batch batch = mAlarmBatches.get(whichBatch);
+ if (batch.add(alarm)) {
+ // The start time of this batch advanced, so batch ordering may
+ // have just been broken. Move it to where it now belongs.
+ mAlarmBatches.remove(whichBatch);
+ addBatchLocked(mAlarmBatches, batch);
+ }
+ }
+ }
+
// Return the index of the matching batch, or -1 if none found.
int attemptCoalesceLocked(long whenElapsed, long maxWhen) {
final int N = mAlarmBatches.size();
@@ -794,7 +818,7 @@
}
void rebatchAllAlarmsLocked(boolean doValidate) {
- long start = mStatLogger.getTime();
+ final long start = mStatLogger.getTime();
final int oldCount =
getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms);
final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches)
@@ -837,6 +861,44 @@
mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start);
}
+ /**
+ * Re-orders the alarm batches based on newly evaluated send times based on the current
+ * app-standby buckets
+ * @param targetPackages [Package, User] pairs for which alarms need to be re-evaluated,
+ * null indicates all
+ * @return True if there was any reordering done to the current list.
+ */
+ boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<Pair<String, Integer>> targetPackages) {
+ final long start = mStatLogger.getTime();
+ final ArrayList<Alarm> rescheduledAlarms = new ArrayList<>();
+
+ for (int batchIndex = mAlarmBatches.size() - 1; batchIndex >= 0; batchIndex--) {
+ final Batch batch = mAlarmBatches.get(batchIndex);
+ for (int alarmIndex = batch.size() - 1; alarmIndex >= 0; alarmIndex--) {
+ final Alarm alarm = batch.get(alarmIndex);
+ final Pair<String, Integer> packageUser =
+ Pair.create(alarm.sourcePackage, UserHandle.getUserId(alarm.creatorUid));
+ if (targetPackages != null && !targetPackages.contains(packageUser)) {
+ continue;
+ }
+ if (adjustDeliveryTimeBasedOnStandbyBucketLocked(alarm)) {
+ batch.remove(alarm);
+ rescheduledAlarms.add(alarm);
+ }
+ }
+ if (batch.size() == 0) {
+ mAlarmBatches.remove(batchIndex);
+ }
+ }
+ for (int i = 0; i < rescheduledAlarms.size(); i++) {
+ final Alarm a = rescheduledAlarms.get(i);
+ insertAndBatchAlarmLocked(a);
+ }
+
+ mStatLogger.logDurationStat(Stats.REORDER_ALARMS_FOR_STANDBY, start);
+ return rescheduledAlarms.size() > 0;
+ }
+
void reAddAlarmLocked(Alarm a, long nowElapsed, boolean doValidate) {
a.when = a.origWhen;
long whenElapsed = convertToElapsed(a.when, a.type);
@@ -950,7 +1012,7 @@
// Recurring alarms may have passed several alarm intervals while the
// alarm was kept pending. Send the appropriate trigger count.
if (alarm.repeatInterval > 0) {
- alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
final long nextElapsed = alarm.whenElapsed + delta;
@@ -1442,18 +1504,27 @@
else return mConstants.APP_STANDBY_MIN_DELAYS[0];
}
- private void adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
- if (alarm.alarmClock != null || UserHandle.isCore(alarm.creatorUid)) {
- return;
+ /**
+ * Adjusts the alarm delivery time based on the current app standby bucket.
+ * @param alarm The alarm to adjust
+ * @return true if the alarm delivery time was updated.
+ */
+ private boolean adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
+ if (isExemptFromAppStandby(alarm)) {
+ return false;
}
if (mAppStandbyParole) {
- if (alarm.whenElapsed > alarm.requestedWhenElapsed) {
- // We did throttle this alarm earlier, restore original requirements
- alarm.whenElapsed = alarm.requestedWhenElapsed;
- alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+ if (alarm.whenElapsed > alarm.expectedWhenElapsed) {
+ // We did defer this alarm earlier, restore original requirements
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
+ return true;
}
- return;
+ return false;
}
+ final long oldWhenElapsed = alarm.whenElapsed;
+ final long oldMaxWhenElapsed = alarm.maxWhenElapsed;
+
final String sourcePackage = alarm.sourcePackage;
final int sourceUserId = UserHandle.getUserId(alarm.creatorUid);
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
@@ -1463,10 +1534,16 @@
final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
if (lastElapsed > 0) {
final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
- if (alarm.requestedWhenElapsed < minElapsed) {
+ if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+ } else {
+ // app is now eligible to run alarms at the originally requested window.
+ // Restore original requirements in case they were changed earlier.
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
}
+ return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
}
private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
@@ -1521,21 +1598,7 @@
}
}
adjustDeliveryTimeBasedOnStandbyBucketLocked(a);
-
- int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)
- ? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed);
- if (whichBatch < 0) {
- Batch batch = new Batch(a);
- addBatchLocked(mAlarmBatches, batch);
- } else {
- Batch batch = mAlarmBatches.get(whichBatch);
- if (batch.add(a)) {
- // The start time of this batch advanced, so batch ordering may
- // have just been broken. Move it to where it now belongs.
- mAlarmBatches.remove(whichBatch);
- addBatchLocked(mAlarmBatches, batch);
- }
- }
+ insertAndBatchAlarmLocked(a);
if (a.alarmClock != null) {
mNextAlarmClockMayChange = true;
@@ -1661,7 +1724,7 @@
// This means we will allow these alarms to go off as normal even while idle, with no
// timing restrictions.
} else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
- || callingUid == mSystemUiUid
+ || UserHandle.isSameApp(callingUid, mSystemUiUid)
|| mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
@@ -1840,14 +1903,6 @@
if (!blocked) {
pw.println(" none");
}
- pw.print(" mUseAllowWhileIdleShortTime: [");
- for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
- if (mUseAllowWhileIdleShortTime.valueAt(i)) {
- UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
- pw.print(" ");
- }
- }
- pw.println("]");
pw.println(" mLastAlarmDeliveredForPackage:");
for (int i = 0; i < mLastAlarmDeliveredForPackage.size(); i++) {
@@ -1913,14 +1968,32 @@
if (mLastAllowWhileIdleDispatch.size() > 0) {
pw.println(" Last allow while idle dispatch times:");
for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
- pw.print(" UID ");
- UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i));
+ pw.print(" UID ");
+ final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
+ UserHandle.formatUid(pw, uid);
pw.print(": ");
- TimeUtils.formatDuration(mLastAllowWhileIdleDispatch.valueAt(i),
- nowELAPSED, pw);
+ final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
+ TimeUtils.formatDuration(lastTime, nowELAPSED, pw);
+
+ final long minInterval = getWhileIdleMinIntervalLocked(uid);
+ pw.print(" Next allowed:");
+ TimeUtils.formatDuration(lastTime + minInterval, nowELAPSED, pw);
+ pw.print(" (");
+ TimeUtils.formatDuration(minInterval, 0, pw);
+ pw.print(")");
+
pw.println();
}
}
+
+ pw.print(" mUseAllowWhileIdleShortTime: [");
+ for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+ if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+ UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
+ pw.print(" ");
+ }
+ }
+ pw.println("]");
pw.println();
if (mLog.dump(pw, " Recent problems", " ")) {
@@ -2181,13 +2254,23 @@
for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
final long token = proto.start(
AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID,
- mLastAllowWhileIdleDispatch.keyAt(i));
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS,
- mLastAllowWhileIdleDispatch.valueAt(i));
+ final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
+ final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
+
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID, uid);
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
+ lastTime + getWhileIdleMinIntervalLocked(uid));
proto.end(token);
}
+ for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+ if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+ proto.write(AlarmManagerServiceProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
+ mUseAllowWhileIdleShortTime.keyAt(i));
+ }
+ }
+
mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
final FilterStats[] topFilters = new FilterStats[10];
@@ -2866,6 +2949,23 @@
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
+ private long getWhileIdleMinIntervalLocked(int uid) {
+ final boolean dozing = mPendingIdleUntil != null;
+ final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
+ if (!dozing && !ebs) {
+ return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+ }
+ if (dozing) {
+ return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ }
+ if (mUseAllowWhileIdleShortTime.get(uid)) {
+ // if the last allow-while-idle went off while uid was fg, or the uid
+ // recently came into fg, don't block the alarm for long.
+ return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+ }
+ return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ }
+
boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
final long nowRTC) {
boolean hasWakeup = false;
@@ -2891,28 +2991,16 @@
// If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
// schedule such alarms.
final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
- final boolean dozing = mPendingIdleUntil != null;
- final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
- final long minTime;
- if (!dozing && !ebs) {
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
- } else if (dozing) {
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
- } else if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) {
- // if the last allow-while-idle went off while uid was fg, or the uid
- // recently came into fg, don't block the alarm for long.
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
- } else {
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
- }
+ final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
if (nowELAPSED < minTime) {
// Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
// alarm went off for this app. Reschedule the alarm to be in the
// correct time period.
- alarm.whenElapsed = minTime;
+ alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
if (alarm.maxWhenElapsed < minTime) {
alarm.maxWhenElapsed = minTime;
}
+ alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
@@ -2962,7 +3050,7 @@
if (alarm.repeatInterval > 0) {
// this adjustment will be zero if we're late by
// less than one full repeat interval
- alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
@@ -3037,8 +3125,9 @@
public long windowLength;
public long whenElapsed; // 'when' in the elapsed time base
public long maxWhenElapsed; // also in the elapsed time base
- public final long requestedWhenElapsed; // original expiry time requested by the app
- public final long requestedMaxWhenElapsed;
+ // Expected alarm expiry time before app standby deferring is applied.
+ public long expectedWhenElapsed;
+ public long expectedMaxWhenElapsed;
public long repeatInterval;
public PriorityClass priorityClass;
@@ -3052,10 +3141,10 @@
|| _type == AlarmManager.RTC_WAKEUP;
when = _when;
whenElapsed = _whenElapsed;
- requestedWhenElapsed = _whenElapsed;
+ expectedWhenElapsed = _whenElapsed;
windowLength = _windowLength;
maxWhenElapsed = _maxWhen;
- requestedMaxWhenElapsed = _maxWhen;
+ expectedMaxWhenElapsed = _maxWhen;
repeatInterval = _interval;
operation = _op;
listener = _rec;
@@ -3114,8 +3203,10 @@
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
pw.print(prefix); pw.print("tag="); pw.println(statsTag);
pw.print(prefix); pw.print("type="); pw.print(type);
- pw.print(" requestedWhenELapsed="); TimeUtils.formatDuration(
- requestedWhenElapsed, nowELAPSED, pw);
+ pw.print(" expectedWhenElapsed="); TimeUtils.formatDuration(
+ expectedWhenElapsed, nowELAPSED, pw);
+ pw.print(" expectedMaxWhenElapsed="); TimeUtils.formatDuration(
+ expectedMaxWhenElapsed, nowELAPSED, pw);
pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
nowELAPSED, pw);
pw.print(" when=");
@@ -3253,6 +3344,11 @@
}
}
+ private boolean isExemptFromAppStandby(Alarm a) {
+ return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
+ || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
+ }
+
private class AlarmThread extends Thread
{
public AlarmThread()
@@ -3367,28 +3463,19 @@
}
mPendingNonWakeupAlarms.clear();
}
- boolean needRebatch = false;
- final HashSet<String> triggerPackages = new HashSet<>();
- for (int i = triggerList.size() - 1; i >= 0; i--) {
- triggerPackages.add(triggerList.get(i).sourcePackage);
- }
- outer:
- for (int i = 0; i < mAlarmBatches.size(); i++) {
- final Batch batch = mAlarmBatches.get(i);
- for (int j = 0; j < batch.size(); j++) {
- if (triggerPackages.contains(batch.get(j))) {
- needRebatch = true;
- break outer;
- }
+ final ArraySet<Pair<String, Integer>> triggerPackages =
+ new ArraySet<>();
+ for (int i = 0; i < triggerList.size(); i++) {
+ final Alarm a = triggerList.get(i);
+ if (!isExemptFromAppStandby(a)) {
+ triggerPackages.add(Pair.create(
+ a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
}
- if (needRebatch) {
- rebatchAllAlarmsLocked(false);
- } else {
- rescheduleKernelAlarmsLocked();
- updateNextAlarmClockLocked();
- }
deliverAlarmsLocked(triggerList, nowELAPSED);
+ reorderAlarmsBasedOnStandbyBuckets(triggerPackages);
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
}
}
@@ -3446,10 +3533,15 @@
public static final int REPORT_ALARMS_ACTIVE = 4;
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
public static final int APP_STANDBY_PAROLE_CHANGED = 6;
+ public static final int REMOVE_FOR_STOPPED = 7;
public AlarmHandler() {
}
+ public void postRemoveForStopped(int uid) {
+ obtainMessage(REMOVE_FOR_STOPPED, uid, 0).sendToTarget();
+ }
+
public void handleMessage(Message msg) {
switch (msg.what) {
case ALARM_EVENT: {
@@ -3494,13 +3586,27 @@
case APP_STANDBY_PAROLE_CHANGED:
synchronized (mLock) {
mAppStandbyParole = (Boolean) msg.obj;
- rebatchAllAlarmsLocked(false);
+ if (reorderAlarmsBasedOnStandbyBuckets(null)) {
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
}
break;
case APP_STANDBY_BUCKET_CHANGED:
synchronized (mLock) {
- rebatchAllAlarmsLocked(false);
+ final ArraySet<Pair<String, Integer>> filterPackages = new ArraySet<>();
+ filterPackages.add(Pair.create((String) msg.obj, msg.arg1));
+ if (reorderAlarmsBasedOnStandbyBuckets(filterPackages)) {
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+ break;
+
+ case REMOVE_FOR_STOPPED:
+ synchronized (mLock) {
+ removeForStoppedLocked(msg.arg1);
}
break;
@@ -3641,6 +3747,7 @@
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
if (uid >= 0) {
mLastAllowWhileIdleDispatch.delete(uid);
+ mUseAllowWhileIdleShortTime.delete(uid);
}
} else {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
@@ -3692,25 +3799,17 @@
}
@Override public void onUidGone(int uid, boolean disabled) {
- synchronized (mLock) {
- mUseAllowWhileIdleShortTime.delete(uid);
- if (disabled) {
- removeForStoppedLocked(uid);
- }
+ if (disabled) {
+ mHandler.postRemoveForStopped(uid);
}
}
@Override public void onUidActive(int uid) {
- synchronized (mLock) {
- mUseAllowWhileIdleShortTime.put(uid, true);
- }
}
@Override public void onUidIdle(int uid, boolean disabled) {
- synchronized (mLock) {
- if (disabled) {
- removeForStoppedLocked(uid);
- }
+ if (disabled) {
+ mHandler.postRemoveForStopped(uid);
}
}
@@ -3730,7 +3829,8 @@
bucket);
}
mHandler.removeMessages(AlarmHandler.APP_STANDBY_BUCKET_CHANGED);
- mHandler.sendEmptyMessage(AlarmHandler.APP_STANDBY_BUCKET_CHANGED);
+ mHandler.obtainMessage(AlarmHandler.APP_STANDBY_BUCKET_CHANGED, userId, -1, packageName)
+ .sendToTarget();
}
@Override
@@ -3766,6 +3866,18 @@
sendPendingBackgroundAlarmsLocked(uid, packageName);
}
}
+
+ @Override
+ public void onUidForeground(int uid, boolean foreground) {
+ synchronized (mLock) {
+ if (foreground) {
+ mUseAllowWhileIdleShortTime.put(uid, true);
+
+ // Note we don't have to drain the pending while-idle alarms here, because
+ // this event should coincide with unblockAlarmsForUid().
+ }
+ }
+ }
};
private final BroadcastStats getStatsLocked(PendingIntent pi) {
@@ -3949,6 +4061,7 @@
/**
* Deliver an alarm and set up the post-delivery handling appropriately
*/
+ @GuardedBy("mLock")
public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
if (alarm.operation != null) {
// PendingIntent alarm
@@ -4026,7 +4139,7 @@
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
- if (mForceAppStandbyTracker.isInForeground(alarm.creatorUid)) {
+ if (mForceAppStandbyTracker.isUidInForeground(alarm.creatorUid)) {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
} else {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
@@ -4041,7 +4154,7 @@
mAllowWhileIdleDispatches.add(ent);
}
}
- if (!UserHandle.isCore(alarm.creatorUid)) {
+ if (!isExemptFromAppStandby(alarm)) {
final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid));
mLastAlarmDeliveredForPackage.put(packageUser, nowELAPSED);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 219facd..0502117 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,7 +34,7 @@
2731 power_soft_sleep_requested (savedwaketimems|2)
# Power save state has changed. See BatterySaverController.java for the details.
2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
-27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6)
+27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
#
# Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 7604044..b65d126 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -54,6 +54,7 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.DeviceIdleController.LocalService;
import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
@@ -64,18 +65,15 @@
/**
* Class to keep track of the information related to "force app standby", which includes:
* - OP_RUN_ANY_IN_BACKGROUND for each package
- * - UID foreground state
+ * - UID foreground/active state
* - User+system power save whitelist
* - Temporary power save whitelist
* - Global "force all apps standby" mode enforced by battery saver.
*
- * TODO: In general, we can reduce the number of callbacks by checking all signals before sending
- * each callback. For example, even when an UID comes into the foreground, if it wasn't
- * originally restricted, then there's no need to send an event.
- * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
+ * TODO: Make it a LocalService.
*
* Test:
- * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
*/
public class ForceAppStandbyTracker {
private static final String TAG = "ForceAppStandbyTracker";
@@ -108,6 +106,11 @@
@GuardedBy("mLock")
final ArraySet<Pair<Integer, String>> mRunAnyRestrictedPackages = new ArraySet<>();
+ /** UIDs that are active. */
+ @GuardedBy("mLock")
+ final SparseBooleanArray mActiveUids = new SparseBooleanArray();
+
+ /** UIDs that are in the foreground. */
@GuardedBy("mLock")
final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
@@ -160,18 +163,20 @@
boolean mForcedAppStandbyEnabled;
interface Stats {
- int UID_STATE_CHANGED = 0;
- int RUN_ANY_CHANGED = 1;
- int ALL_UNWHITELISTED = 2;
- int ALL_WHITELIST_CHANGED = 3;
- int TEMP_WHITELIST_CHANGED = 4;
- int EXEMPT_CHANGED = 5;
- int FORCE_ALL_CHANGED = 6;
- int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 7;
+ int UID_FG_STATE_CHANGED = 0;
+ int UID_ACTIVE_STATE_CHANGED = 1;
+ int RUN_ANY_CHANGED = 2;
+ int ALL_UNWHITELISTED = 3;
+ int ALL_WHITELIST_CHANGED = 4;
+ int TEMP_WHITELIST_CHANGED = 5;
+ int EXEMPT_CHANGED = 6;
+ int FORCE_ALL_CHANGED = 7;
+ int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
}
private final StatLogger mStatLogger = new StatLogger(new String[] {
- "UID_STATE_CHANGED",
+ "UID_FG_STATE_CHANGED",
+ "UID_ACTIVE_STATE_CHANGED",
"RUN_ANY_CHANGED",
"ALL_UNWHITELISTED",
"ALL_WHITELIST_CHANGED",
@@ -260,9 +265,16 @@
* This is called when the foreground state changed for a UID.
*/
private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
+ onUidForeground(uid, sender.isUidInForeground(uid));
+ }
+
+ /**
+ * This is called when the active/idle state changed for a UID.
+ */
+ private void onUidActiveStateChanged(ForceAppStandbyTracker sender, int uid) {
updateJobsForUid(uid);
- if (sender.isInForeground(uid)) {
+ if (sender.isUidActive(uid)) {
unblockAlarmsForUid(uid);
}
}
@@ -355,6 +367,14 @@
*/
public void unblockAlarmsForUidPackage(int uid, String packageName) {
}
+
+ /**
+ * Called when a UID comes into the foreground or the background.
+ *
+ * @see #isUidInForeground(int)
+ */
+ public void onUidForeground(int uid, boolean foreground) {
+ }
}
@VisibleForTesting
@@ -404,8 +424,10 @@
try {
mIActivityManager.registerUidObserver(new UidObserver(),
- ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.UID_OBSERVER_GONE
+ | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_ACTIVE
+ | ActivityManager.UID_OBSERVER_PROCSTATE,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
mAppOpsService.startWatchingMode(TARGET_OP, null,
new AppOpsWatcher());
@@ -475,6 +497,7 @@
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
+ @GuardedBy("mLock")
private void refreshForcedAppStandbyUidPackagesLocked() {
mRunAnyRestrictedPackages.clear();
final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(
@@ -514,6 +537,7 @@
/**
* Update {@link #mForceAllAppsStandby} and notifies the listeners.
*/
+ @GuardedBy("mLock")
private void toggleForceAllAppsStandbyLocked(boolean enable) {
if (enable == mForceAllAppsStandby) {
return;
@@ -523,6 +547,7 @@
mHandler.notifyForceAllAppsStandbyChanged();
}
+ @GuardedBy("mLock")
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
final int size = mRunAnyRestrictedPackages.size();
if (size > 8) {
@@ -541,6 +566,7 @@
/**
* @return whether a uid package-name pair is in mRunAnyRestrictedPackages.
*/
+ @GuardedBy("mLock")
boolean isRunAnyRestrictedLocked(int uid, @NonNull String packageName) {
return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
}
@@ -548,6 +574,7 @@
/**
* Add to / remove from {@link #mRunAnyRestrictedPackages}.
*/
+ @GuardedBy("mLock")
boolean updateForcedAppStandbyUidPackageLocked(int uid, @NonNull String packageName,
boolean restricted) {
final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
@@ -563,65 +590,51 @@
return true;
}
- /**
- * Puts a UID to {@link #mForegroundUids}.
- */
- void uidToForeground(int uid) {
- synchronized (mLock) {
- if (UserHandle.isCore(uid)) {
- return;
- }
- // TODO This can be optimized by calling indexOfKey and sharing the index for get and
- // put.
- if (mForegroundUids.get(uid)) {
- return;
- }
- mForegroundUids.put(uid, true);
- mHandler.notifyUidForegroundStateChanged(uid);
+ private static boolean addUidToArray(SparseBooleanArray array, int uid) {
+ if (UserHandle.isCore(uid)) {
+ return false;
}
+ if (array.get(uid)) {
+ return false;
+ }
+ array.put(uid, true);
+ return true;
}
- /**
- * Sets false for a UID {@link #mForegroundUids}, or remove it when {@code remove} is true.
- */
- void uidToBackground(int uid, boolean remove) {
- synchronized (mLock) {
- if (UserHandle.isCore(uid)) {
- return;
- }
- // TODO This can be optimized by calling indexOfKey and sharing the index for get and
- // put.
- if (!mForegroundUids.get(uid)) {
- return;
- }
- if (remove) {
- mForegroundUids.delete(uid);
- } else {
- mForegroundUids.put(uid, false);
- }
- mHandler.notifyUidForegroundStateChanged(uid);
+ private static boolean removeUidFromArray(SparseBooleanArray array, int uid, boolean remove) {
+ if (UserHandle.isCore(uid)) {
+ return false;
}
+ if (!array.get(uid)) {
+ return false;
+ }
+ if (remove) {
+ array.delete(uid);
+ } else {
+ array.put(uid, false);
+ }
+ return true;
}
private final class UidObserver extends IUidObserver.Stub {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
- }
-
- @Override
- public void onUidGone(int uid, boolean disabled) {
- uidToBackground(uid, /*remove=*/ true);
+ mHandler.onUidStateChanged(uid, procState);
}
@Override
public void onUidActive(int uid) {
- uidToForeground(uid);
+ mHandler.onUidActive(uid);
+ }
+
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ mHandler.onUidGone(uid, disabled);
}
@Override
public void onUidIdle(int uid, boolean disabled) {
- // Just to avoid excessive memcpy, don't remove from the array in this case.
- uidToBackground(uid, /*remove=*/ false);
+ mHandler.onUidIdle(uid, disabled);
}
@Override
@@ -695,22 +708,32 @@
}
private class MyHandler extends Handler {
- private static final int MSG_UID_STATE_CHANGED = 1;
- private static final int MSG_RUN_ANY_CHANGED = 2;
- private static final int MSG_ALL_UNWHITELISTED = 3;
- private static final int MSG_ALL_WHITELIST_CHANGED = 4;
- private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
- private static final int MSG_FORCE_ALL_CHANGED = 6;
- private static final int MSG_USER_REMOVED = 7;
- private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
- private static final int MSG_EXEMPT_CHANGED = 9;
+ private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
+ private static final int MSG_UID_FG_STATE_CHANGED = 1;
+ private static final int MSG_RUN_ANY_CHANGED = 3;
+ private static final int MSG_ALL_UNWHITELISTED = 4;
+ private static final int MSG_ALL_WHITELIST_CHANGED = 5;
+ private static final int MSG_TEMP_WHITELIST_CHANGED = 6;
+ private static final int MSG_FORCE_ALL_CHANGED = 7;
+ private static final int MSG_USER_REMOVED = 8;
+ private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
+ private static final int MSG_EXEMPT_CHANGED = 10;
+
+ private static final int MSG_ON_UID_STATE_CHANGED = 11;
+ private static final int MSG_ON_UID_ACTIVE = 12;
+ private static final int MSG_ON_UID_GONE = 13;
+ private static final int MSG_ON_UID_IDLE = 14;
public MyHandler(Looper looper) {
super(looper);
}
+ public void notifyUidActiveStateChanged(int uid) {
+ obtainMessage(MSG_UID_ACTIVE_STATE_CHANGED, uid, 0).sendToTarget();
+ }
+
public void notifyUidForegroundStateChanged(int uid) {
- obtainMessage(MSG_UID_STATE_CHANGED, uid, 0).sendToTarget();
+ obtainMessage(MSG_UID_FG_STATE_CHANGED, uid, 0).sendToTarget();
}
public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
@@ -718,26 +741,32 @@
}
public void notifyAllUnwhitelisted() {
+ removeMessages(MSG_ALL_UNWHITELISTED);
obtainMessage(MSG_ALL_UNWHITELISTED).sendToTarget();
}
public void notifyAllWhitelistChanged() {
+ removeMessages(MSG_ALL_WHITELIST_CHANGED);
obtainMessage(MSG_ALL_WHITELIST_CHANGED).sendToTarget();
}
public void notifyTempWhitelistChanged() {
+ removeMessages(MSG_TEMP_WHITELIST_CHANGED);
obtainMessage(MSG_TEMP_WHITELIST_CHANGED).sendToTarget();
}
public void notifyForceAllAppsStandbyChanged() {
+ removeMessages(MSG_FORCE_ALL_CHANGED);
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
public void notifyForcedAppStandbyFeatureFlagChanged() {
+ removeMessages(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED);
obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
}
public void notifyExemptChanged() {
+ removeMessages(MSG_EXEMPT_CHANGED);
obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget();
}
@@ -745,6 +774,22 @@
obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget();
}
+ public void onUidStateChanged(int uid, int procState) {
+ obtainMessage(MSG_ON_UID_STATE_CHANGED, uid, procState).sendToTarget();
+ }
+
+ public void onUidActive(int uid) {
+ obtainMessage(MSG_ON_UID_ACTIVE, uid, 0).sendToTarget();
+ }
+
+ public void onUidGone(int uid, boolean disabled) {
+ obtainMessage(MSG_ON_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
+ }
+
+ public void onUidIdle(int uid, boolean disabled) {
+ obtainMessage(MSG_ON_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -763,11 +808,18 @@
long start = mStatLogger.getTime();
switch (msg.what) {
- case MSG_UID_STATE_CHANGED:
+ case MSG_UID_ACTIVE_STATE_CHANGED:
+ for (Listener l : cloneListeners()) {
+ l.onUidActiveStateChanged(sender, msg.arg1);
+ }
+ mStatLogger.logDurationStat(Stats.UID_ACTIVE_STATE_CHANGED, start);
+ return;
+
+ case MSG_UID_FG_STATE_CHANGED:
for (Listener l : cloneListeners()) {
l.onUidForegroundStateChanged(sender, msg.arg1);
}
- mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start);
+ mStatLogger.logDurationStat(Stats.UID_FG_STATE_CHANGED, start);
return;
case MSG_RUN_ANY_CHANGED:
@@ -831,6 +883,61 @@
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
return;
+
+ case MSG_ON_UID_STATE_CHANGED:
+ handleUidStateChanged(msg.arg1, msg.arg2);
+ return;
+ case MSG_ON_UID_ACTIVE:
+ handleUidActive(msg.arg1);
+ return;
+ case MSG_ON_UID_GONE:
+ handleUidGone(msg.arg1, msg.arg1 != 0);
+ return;
+ case MSG_ON_UID_IDLE:
+ handleUidIdle(msg.arg1, msg.arg1 != 0);
+ return;
+ }
+ }
+
+ public void handleUidStateChanged(int uid, int procState) {
+ synchronized (mLock) {
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ if (removeUidFromArray(mForegroundUids, uid, false)) {
+ mHandler.notifyUidForegroundStateChanged(uid);
+ }
+ } else {
+ if (addUidToArray(mForegroundUids, uid)) {
+ mHandler.notifyUidForegroundStateChanged(uid);
+ }
+ }
+ }
+ }
+
+ public void handleUidActive(int uid) {
+ synchronized (mLock) {
+ if (addUidToArray(mActiveUids, uid)) {
+ mHandler.notifyUidActiveStateChanged(uid);
+ }
+ }
+ }
+
+ public void handleUidGone(int uid, boolean disabled) {
+ removeUid(uid, true);
+ }
+
+ public void handleUidIdle(int uid, boolean disabled) {
+ // Just to avoid excessive memcpy, don't remove from the array in this case.
+ removeUid(uid, false);
+ }
+
+ private void removeUid(int uid, boolean remove) {
+ synchronized (mLock) {
+ if (removeUidFromArray(mActiveUids, uid, remove)) {
+ mHandler.notifyUidActiveStateChanged(uid);
+ }
+ if (removeUidFromArray(mForegroundUids, uid, remove)) {
+ mHandler.notifyUidForegroundStateChanged(uid);
+ }
}
}
}
@@ -846,18 +953,23 @@
mRunAnyRestrictedPackages.removeAt(i);
}
}
- for (int i = mForegroundUids.size() - 1; i >= 0; i--) {
- final int uid = mForegroundUids.keyAt(i);
- final int userId = UserHandle.getUserId(uid);
-
- if (userId == removedUserId) {
- mForegroundUids.removeAt(i);
- }
- }
+ cleanUpArrayForUser(mActiveUids, removedUserId);
+ cleanUpArrayForUser(mForegroundUids, removedUserId);
mExemptedPackages.remove(removedUserId);
}
}
+ private void cleanUpArrayForUser(SparseBooleanArray array, int removedUserId) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final int uid = array.keyAt(i);
+ final int userId = UserHandle.getUserId(uid);
+
+ if (userId == removedUserId) {
+ array.removeAt(i);
+ }
+ }
+ }
+
/**
* Called by device idle controller to update the power save whitelists.
*/
@@ -954,7 +1066,7 @@
*/
private boolean isRestricted(int uid, @NonNull String packageName,
boolean useTempWhitelistToo, boolean exemptOnBatterySaver) {
- if (isInForeground(uid)) {
+ if (isUidActive(uid)) {
return false;
}
synchronized (mLock) {
@@ -982,13 +1094,29 @@
}
/**
+ * @return whether a UID is in active or not.
+ *
+ * Note this information is based on the UID proc state callback, meaning it's updated
+ * asynchronously and may subtly be stale. If the fresh data is needed, use
+ * {@link ActivityManagerInternal#getUidProcessState} instead.
+ */
+ public boolean isUidActive(int uid) {
+ if (UserHandle.isCore(uid)) {
+ return true;
+ }
+ synchronized (mLock) {
+ return mActiveUids.get(uid);
+ }
+ }
+
+ /**
* @return whether a UID is in the foreground or not.
*
* Note this information is based on the UID proc state callback, meaning it's updated
* asynchronously and may subtly be stale. If the fresh data is needed, use
* {@link ActivityManagerInternal#getUidProcessState} instead.
*/
- public boolean isInForeground(int uid) {
+ public boolean isUidInForeground(int uid) {
if (UserHandle.isCore(uid)) {
return true;
}
@@ -1062,17 +1190,12 @@
pw.println(mIsPluggedIn);
pw.print(indent);
- pw.print("Foreground uids: [");
+ pw.print("Active uids: ");
+ dumpUids(pw, mActiveUids);
- String sep = "";
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) {
- pw.print(sep);
- pw.print(UserHandle.formatUid(mForegroundUids.keyAt(i)));
- sep = " ";
- }
- }
- pw.println("]");
+ pw.print(indent);
+ pw.print("Foreground uids: ");
+ dumpUids(pw, mForegroundUids);
pw.print(indent);
pw.print("Whitelist appids: ");
@@ -1114,6 +1237,20 @@
}
}
+ private void dumpUids(PrintWriter pw, SparseBooleanArray array) {
+ pw.print("[");
+
+ String sep = "";
+ for (int i = 0; i < array.size(); i++) {
+ if (array.valueAt(i)) {
+ pw.print(sep);
+ pw.print(UserHandle.formatUid(array.keyAt(i)));
+ sep = " ";
+ }
+ }
+ pw.println("]");
+ }
+
public void dumpProto(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
final long token = proto.start(fieldId);
@@ -1125,6 +1262,13 @@
mForceAllAppStandbyForSmallBattery);
proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn);
+ for (int i = 0; i < mActiveUids.size(); i++) {
+ if (mActiveUids.valueAt(i)) {
+ proto.write(ForceAppStandbyTrackerProto.ACTIVE_UIDS,
+ mActiveUids.keyAt(i));
+ }
+ }
+
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
proto.write(ForceAppStandbyTrackerProto.FOREGROUND_UIDS,
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fc91d0d..bdeb231 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -59,6 +59,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -468,7 +469,7 @@
int mCurFocusedWindowSoftInputMode;
/**
- * The client by which {@link #mCurFocusedWindow} was reported. Used only for debugging.
+ * The client by which {@link #mCurFocusedWindow} was reported.
*/
ClientState mCurFocusedWindowClient;
@@ -1165,6 +1166,7 @@
mImePackageAppeared = false;
}
+ @GuardedBy("mMethodMap")
private boolean shouldRebuildInputMethodListLocked() {
// This method is guaranteed to be called only by getRegisteredHandler().
@@ -1466,6 +1468,7 @@
setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
+ @GuardedBy("mMethodMap")
private void switchUserLocked(int newUserId) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
@@ -1816,6 +1819,7 @@
return flags;
}
+ @GuardedBy("mMethodMap")
@NonNull
InputBindResult attachNewInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
@@ -1845,6 +1849,7 @@
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
+ @GuardedBy("mMethodMap")
@NonNull
InputBindResult startInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
@@ -1888,6 +1893,7 @@
controlFlags, startInputReason);
}
+ @GuardedBy("mMethodMap")
@NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@@ -2969,12 +2975,19 @@
break;
}
- if (!didStart && attribute != null) {
- if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
- || (controlFlags
- & InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0) {
- res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
- controlFlags, startInputReason);
+ if (!didStart) {
+ if (attribute != null) {
+ if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
+ || (controlFlags
+ & InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0) {
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+ attribute,
+ controlFlags, startInputReason);
+ } else {
+ res = InputBindResult.NO_EDITOR;
+ }
+ } else {
+ res = InputBindResult.NULL_EDITOR_INFO;
}
}
}
@@ -2989,8 +3002,8 @@
final int uid = Binder.getCallingUid();
if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
return true;
- } else if (mCurClient != null && client != null
- && mCurClient.client.asBinder() == client.asBinder()) {
+ } else if (mCurFocusedWindowClient != null && client != null
+ && mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
return true;
} else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
mAppOpsManager,
@@ -3026,6 +3039,15 @@
}
}
+ public boolean isInputMethodPickerShownForTest() {
+ synchronized(mMethodMap) {
+ if (mSwitchingDialog == null) {
+ return false;
+ }
+ return mSwitchingDialog.isShowing();
+ }
+ }
+
@Override
public void setInputMethod(IBinder token, String id) {
if (!calledFromValidUser()) {
@@ -3625,6 +3647,7 @@
return false;
}
+ @GuardedBy("mMethodMap")
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 1dd92f3..65e90bad 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1397,23 +1397,6 @@
}
/**
- * Returns "true" if access to the specified location provider is allowed by the specified
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- * @param uid the requestor's UID
- * @param userId the user id to query
- */
- private boolean isAllowedByUserSettingsLockedForUser(
- String provider, int uid, int userId) {
- if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
- return false;
- }
- return isLocationProviderEnabledForUser(provider, userId);
- }
-
- /**
* Returns the permission string associated with the specified resolution level.
*
* @param resolutionLevel the resolution level
@@ -2585,143 +2568,6 @@
}
/**
- * Method for enabling or disabling location.
- *
- * @param enabled true to enable location. false to disable location
- * @param userId the user id to set
- */
- @Override
- public void setLocationEnabledForUser(boolean enabled, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Enable or disable all location providers. Fused provider and passive provider are
- // excluded.
- synchronized (mLock) {
- for(String provider : getAllProvidersForLocationSettings()) {
- setProviderEnabledForUser(provider, enabled, userId);
- }
- }
- }
-
- /**
- * Returns the current enabled/disabled status of location
- *
- * @param userId the user id to query
- * @return true if location is enabled. false if location is disabled.
- */
- @Override
- public boolean isLocationEnabledForUser(int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // If at least one location provider is enabled, return true. Fused provider and passive
- // provider are excluded.
- synchronized (mLock) {
- for (String provider : getAllProvidersForLocationSettings()) {
- if (isProviderEnabledForUser(provider, userId)) {
- return true;
- }
- }
- return false;
- }
- }
-
- @Override
- public boolean isProviderEnabled(String provider) {
- return isProviderEnabledForUser(provider, UserHandle.getCallingUserId());
- }
-
- /**
- * Method for determining if a location provider is enabled.
- *
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
- */
- @Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Method for enabling or disabling a single location provider.
- *
- * @param provider the name of the provider
- * @param enabled true to enable the provider. false to disable the provider
- * @param userId the user id to set
- * @return true if the value was set successfully. false on failure.
- */
- @Override
- public boolean setProviderEnabledForUser(
- String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- if (enabled) {
- provider = "+" + provider;
- } else {
- provider = "-" + provider;
- }
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- provider,
- userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Return all location providers except fused provider and passive provider. These two
- * providers are not generating location by themselves, but only echo locations from other
- * providers.
- *
- * @return All location providers except fused provider and passive provider, including
- * providers that are not permitted to be accessed by the calling activity or are
- * currently disabled.
- */
- private List<String> getAllProvidersForLocationSettings() {
- List<String> providersForSettings = new ArrayList<>(mProviders.size());
- for (String provider : getAllProviders()) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)) {
- continue;
- }
- providersForSettings.add(provider);
- }
- return providersForSettings;
- }
-
- /**
* Read location provider status from Settings.Secure
*
* @param provider the location provider to query
@@ -2742,23 +2588,6 @@
}
/**
- * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
- * current user id
- *
- * @param userId the user id to get or set value
- */
- private void checkInteractAcrossUsersPermission(int userId) {
- int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- if (ActivityManager.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
- }
- }
- }
-
- /**
* Returns "true" if the UID belongs to a bound location provider.
*
* @param uid the uid
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a2c0506..3ca3a96 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,16 +1,13 @@
# Connectivity / Networking
per-file ConnectivityService.java=ek@google.com
-per-file ConnectivityService.java=hugobenichi@google.com
per-file ConnectivityService.java=jchalard@google.com
per-file ConnectivityService.java=lorenzo@google.com
per-file ConnectivityService.java=satk@google.com
per-file NetworkManagementService.java=ek@google.com
-per-file NetworkManagementService.java=hugobenichi@google.com
per-file NetworkManagementService.java=jchalard@google.com
per-file NetworkManagementService.java=lorenzo@google.com
per-file NetworkManagementService.java=satk@google.com
per-file NsdService.java=ek@google.com
-per-file NsdService.java=hugobenichi@google.com
per-file NsdService.java=jchalard@google.com
per-file NsdService.java=lorenzo@google.com
per-file NsdService.java=satk@google.com
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index f4238f2..42c836e 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -199,6 +199,7 @@
* bound.
* @returns {@code true} if a valid package was found to bind to.
*/
+ @GuardedBy("mLock")
private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
Intent intent = new Intent(mAction);
if (justCheckThisPackage != null) {
@@ -273,6 +274,7 @@
return true;
}
+ @GuardedBy("mLock")
private void unbindLocked() {
ComponentName component;
component = mBoundComponent;
@@ -287,6 +289,7 @@
}
}
+ @GuardedBy("mLock")
private void bindToPackageLocked(ComponentName component, int version, int userId) {
Intent intent = new Intent(mAction);
intent.setComponent(component);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 84b93e3..1a0068d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -809,6 +809,7 @@
}
}
+ @GuardedBy("mLock")
private void addInternalVolumeLocked() {
// Create a stub volume that represents internal storage
final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
@@ -1109,6 +1110,7 @@
}
};
+ @GuardedBy("mLock")
private void onDiskScannedLocked(DiskInfo disk) {
int volumeCount = 0;
for (int i = 0; i < mVolumes.size(); i++) {
@@ -1134,6 +1136,7 @@
mCallbacks.notifyDiskScanned(disk, volumeCount);
}
+ @GuardedBy("mLock")
private void onVolumeCreatedLocked(VolumeInfo vol) {
if (mPms.isOnlyCoreApps()) {
Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
@@ -1209,6 +1212,7 @@
return true;
}
+ @GuardedBy("mLock")
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
@@ -1299,6 +1303,7 @@
}
}
+ @GuardedBy("mLock")
private void onMoveStatusLocked(int status) {
if (mMoveCallback == null) {
Slog.w(TAG, "Odd, status but no move requested");
@@ -1515,6 +1520,7 @@
}
}
+ @GuardedBy("mLock")
private void readSettingsLocked() {
mRecords.clear();
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
@@ -1559,6 +1565,7 @@
}
}
+ @GuardedBy("mLock")
private void writeSettingsLocked() {
FileOutputStream fos = null;
try {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 48b5a58..8d22eca 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -55,6 +55,7 @@
import android.view.InputDevice;
import android.media.AudioAttributes;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
@@ -111,6 +112,7 @@
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
+ @GuardedBy("mLock")
private Vibration mCurrentVibration;
private int mCurVibUid = -1;
private boolean mLowPowerMode;
@@ -435,45 +437,45 @@
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
- if (effect instanceof VibrationEffect.OneShot
- && mCurrentVibration != null
- && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
- VibrationEffect.OneShot currentOneShot =
- (VibrationEffect.OneShot) mCurrentVibration.effect;
- if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
- && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+ synchronized (mLock) {
+ if (effect instanceof VibrationEffect.OneShot
+ && mCurrentVibration != null
+ && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
+ VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
+ VibrationEffect.OneShot currentOneShot =
+ (VibrationEffect.OneShot) mCurrentVibration.effect;
+ if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
+ && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ }
+ return;
+ }
+ }
+
+ // If the current vibration is repeating and the incoming one is non-repeating, then
+ // ignore the non-repeating vibration. This is so that we don't cancel vibrations that
+ // are meant to grab the attention of the user, like ringtones and alarms, in favor of
+ // one-shot vibrations that are likely quite short.
+ if (!isRepeatingVibration(effect)
+ && mCurrentVibration != null
+ && isRepeatingVibration(mCurrentVibration.effect)) {
if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
return;
}
- }
- // If the current vibration is repeating and the incoming one is non-repeating, then ignore
- // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
- // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
- // vibrations that are likely quite short.
- if (!isRepeatingVibration(effect)
- && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.effect)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- return;
- }
-
- Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
- linkVibration(vib);
-
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
+ Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
+ linkVibration(vib);
+ long ident = Binder.clearCallingIdentity();
+ try {
doCancelVibrateLocked();
startVibrationLocked(vib);
addToPreviousVibrationsLocked(vib);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
@@ -516,6 +518,7 @@
}
};
+ @GuardedBy("mLock")
private void doCancelVibrateLocked() {
mH.removeCallbacks(mVibrationEndRunnable);
if (mThread != null) {
@@ -538,6 +541,7 @@
}
}
+ @GuardedBy("mLock")
private void startVibrationLocked(final Vibration vib) {
if (!isAllowedToVibrateLocked(vib)) {
return;
@@ -568,6 +572,7 @@
startVibrationInnerLocked(vib);
}
+ @GuardedBy("mLock")
private void startVibrationInnerLocked(Vibration vib) {
mCurrentVibration = vib;
if (vib.effect instanceof VibrationEffect.OneShot) {
@@ -701,6 +706,7 @@
return mode;
}
+ @GuardedBy("mLock")
private void reportFinishVibrationLocked() {
if (mCurrentVibration != null) {
try {
@@ -880,6 +886,7 @@
}
}
+ @GuardedBy("mLock")
private long doVibratorPrebakedEffectLocked(Vibration vib) {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final boolean usingInputDeviceVibrators;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2f7d4c1..14404f5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -84,6 +84,7 @@
import android.util.EventLog;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.StatsLog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -1094,6 +1095,8 @@
active.mNumActive++;
}
r.isForeground = true;
+ StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+ StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
}
r.postNotification();
if (r.app != null) {
@@ -1109,6 +1112,8 @@
decActiveForegroundAppLocked(smap, r);
}
r.isForeground = false;
+ StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+ StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
updateServiceForegroundLocked(r.app, true);
@@ -2533,7 +2538,10 @@
cancelForegroundNotificationLocked(r);
if (r.isForeground) {
decActiveForegroundAppLocked(smap, r);
+ StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.userId, r.shortName,
+ StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
}
+
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5b8b691..2cae1a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -385,6 +385,7 @@
import android.view.View;
import android.view.WindowManager;
+import android.view.autofill.AutofillManagerInternal;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -777,8 +778,11 @@
long mWaitForNetworkTimeoutMs;
/**
- * Helper class which parses out priority arguments and dumps sections according to their
- * priority. If priority arguments are omitted, function calls the legacy dump command.
+ * Helper class which strips out priority and proto arguments then calls the dump function with
+ * the appropriate arguments. If priority arguments are omitted, function calls the legacy
+ * dump command.
+ * If priority arguments are omitted all sections are dumped, otherwise sections are dumped
+ * according to their priority.
*/
private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
@Override
@@ -790,23 +794,7 @@
@Override
public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
- if (asProto) {
- doDump(fd, pw, new String[0], asProto);
- } else {
- doDump(fd, pw, new String[]{"settings"}, asProto);
- doDump(fd, pw, new String[]{"intents"}, asProto);
- doDump(fd, pw, new String[]{"broadcasts"}, asProto);
- doDump(fd, pw, new String[]{"providers"}, asProto);
- doDump(fd, pw, new String[]{"permissions"}, asProto);
- doDump(fd, pw, new String[]{"services"}, asProto);
- doDump(fd, pw, new String[]{"recents"}, asProto);
- doDump(fd, pw, new String[]{"lastanr"}, asProto);
- doDump(fd, pw, new String[]{"starter"}, asProto);
- if (mAssociations.size() > 0) {
- doDump(fd, pw, new String[]{"associations"}, asProto);
- }
- doDump(fd, pw, new String[]{"processes"}, asProto);
- }
+ doDump(fd, pw, new String[]{"-a", "--normal-priority"}, asProto);
}
@Override
@@ -1285,6 +1273,40 @@
}
}
+ DevelopmentSettingsObserver mDevelopmentSettingsObserver;
+
+ private final class DevelopmentSettingsObserver extends ContentObserver {
+ private final Uri mUri = Settings.Global
+ .getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
+
+ private final ComponentName mBugreportStorageProvider = new ComponentName(
+ "com.android.shell", "com.android.shell.BugreportStorageProvider");
+
+ public DevelopmentSettingsObserver() {
+ super(mHandler);
+ mContext.getContentResolver().registerContentObserver(mUri, false, this,
+ UserHandle.USER_ALL);
+ // Always kick once to ensure that we match current state
+ onChange();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ if (mUri.equals(uri)) {
+ onChange();
+ }
+ }
+
+ public void onChange() {
+ final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, Build.IS_ENG ? 1 : 0) != 0;
+ mContext.getPackageManager().setComponentEnabledSetting(mBugreportStorageProvider,
+ enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ 0);
+ }
+ }
+
/**
* Thread-local storage used to carry caller permissions over through
* indirect content-provider access.
@@ -1832,7 +1854,6 @@
static final int REPORT_MEM_USAGE_MSG = 33;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
- static final int REQUEST_ALL_PSS_MSG = 39;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
@@ -2298,12 +2319,6 @@
writeGrantedUriPermissions();
break;
}
- case REQUEST_ALL_PSS_MSG: {
- synchronized (ActivityManagerService.this) {
- requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
- }
- break;
- }
case UPDATE_TIME_PREFERENCE_MSG: {
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
@@ -2593,11 +2608,17 @@
procState = proc.pssProcState;
statType = proc.pssStatType;
lastPssTime = proc.lastPssTime;
+ long now = SystemClock.uptimeMillis();
if (proc.thread != null && procState == proc.setProcState
&& (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
- < SystemClock.uptimeMillis()) {
+ < now) {
pid = proc.pid;
} else {
+ ProcessList.abortNextPssTime(proc.procStateMemTracker);
+ if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
+ ": still need " +
+ (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE-now) +
+ "ms until safe");
proc = null;
pid = 0;
}
@@ -2983,6 +3004,16 @@
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
+
+ // bind background thread to little cores
+ // this is expected to fail inside of framework tests because apps can't touch cpusets directly
+ try {
+ Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
+ Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ } catch (Exception e) {
+ Slog.w(TAG, "Setting background thread cpuset failed");
+ }
+
}
protected ActivityStackSupervisor createStackSupervisor() {
@@ -3261,6 +3292,7 @@
* Update AMS states when an activity is resumed. This should only be called by
* {@link ActivityStack#setResumedActivityLocked} when an activity is resumed.
*/
+ @GuardedBy("this")
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
if (task.isActivityTypeStandard()) {
@@ -3795,6 +3827,7 @@
}
}
+ @GuardedBy("this")
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
@@ -3805,6 +3838,7 @@
null /* crashHandler */);
}
+ @GuardedBy("this")
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
@@ -3921,6 +3955,7 @@
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
+ @GuardedBy("this")
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
@@ -3929,6 +3964,7 @@
/**
* @return {@code true} if process start is successful, false otherwise.
*/
+ @GuardedBy("this")
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride) {
if (app.pendingStart) {
@@ -4027,9 +4063,13 @@
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("true".equals(genDebugInfoProperty)) {
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
@@ -4331,7 +4371,9 @@
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
component.userId, component.realActivity.getPackageName(),
- component.realActivity.getShortClassName(), resumed ? 1 : 0);
+ component.realActivity.getShortClassName(), resumed ?
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -5097,6 +5139,7 @@
.supportsLocalVoiceInteraction();
}
+ @GuardedBy("this")
void onLocalVoiceInteractionStartedLocked(IBinder activity,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
@@ -5595,6 +5638,7 @@
* as a result of that process going away. Clears out all connections
* to the process.
*/
+ @GuardedBy("this")
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
@@ -5741,10 +5785,12 @@
}
}
+ @GuardedBy("this")
final void appDiedLocked(ProcessRecord app) {
appDiedLocked(app, app.pid, app.thread, false);
}
+ @GuardedBy("this")
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied) {
// First check if this ProcessRecord is actually active for the pid.
@@ -6202,6 +6248,7 @@
}
}
+ @GuardedBy("this")
final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
if (!mLaunchWarningShown) {
mLaunchWarningShown = true;
@@ -6628,6 +6675,7 @@
}
}
+ @GuardedBy("this")
void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -6734,11 +6782,13 @@
}
}
+ @GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
false, true, false, false, UserHandle.getUserId(uid), reason);
}
+ @GuardedBy("this")
private void finishForceStopPackageLocked(final String packageName, int uid) {
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
@@ -6754,6 +6804,7 @@
}
+ @GuardedBy("this")
private final boolean killPackageProcessesLocked(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
boolean doit, boolean evenPersistent, String reason) {
@@ -6925,6 +6976,7 @@
return didSomething;
}
+ @GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
@@ -7136,6 +7188,7 @@
}
}
+ @GuardedBy("this")
boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
@@ -7190,6 +7243,7 @@
return needRestart;
}
+ @GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
removeProcessLocked(app, false, true, "timeout publishing content providers");
@@ -7250,6 +7304,7 @@
}
}
+ @GuardedBy("this")
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
@@ -7472,6 +7527,18 @@
thread.attachAgent(preBindAgent);
}
+
+ // Figure out whether the app needs to run in autofill compat mode.
+ boolean isAutofillCompatEnabled = false;
+ if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) {
+ final AutofillManagerInternal afm = LocalServices.getService(
+ AutofillManagerInternal.class);
+ if (afm != null) {
+ isAutofillCompatEnabled = afm.isCompatibilityModeRequested(
+ app.info.packageName, app.info.versionCode, app.userId);
+ }
+ }
+
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
if (app.isolatedEntryPoint != null) {
@@ -7489,7 +7556,7 @@
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
- buildSerial);
+ buildSerial, isAutofillCompatEnabled);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
@@ -7498,7 +7565,7 @@
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
- buildSerial);
+ buildSerial, isAutofillCompatEnabled);
}
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
@@ -9091,6 +9158,7 @@
grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
}
+ @GuardedBy("this")
private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris != null) {
@@ -9099,6 +9167,7 @@
return null;
}
+ @GuardedBy("this")
private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
String targetPkg, int targetUid, GrantUri grantUri) {
ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
@@ -9116,6 +9185,7 @@
return perm;
}
+ @GuardedBy("this")
private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
final int modeFlags) {
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
@@ -9186,6 +9256,7 @@
* If you already know the uid of the target, you can supply it in
* lastTargetUid else set that to -1.
*/
+ @GuardedBy("this")
int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
final int modeFlags, int lastTargetUid) {
if (!Intent.isAccessUriMode(modeFlags)) {
@@ -9348,6 +9419,7 @@
}
}
+ @GuardedBy("this")
void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri,
final int modeFlags, UriPermissionOwner owner) {
if (!Intent.isAccessUriMode(modeFlags)) {
@@ -9377,6 +9449,7 @@
perm.grantModes(modeFlags, owner);
}
+ @GuardedBy("this")
void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
final int modeFlags, UriPermissionOwner owner, int targetUserId) {
if (targetPkg == null) {
@@ -9428,6 +9501,7 @@
/**
* Like checkGrantUriPermissionLocked, but takes an Intent.
*/
+ @GuardedBy("this")
NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
@@ -9514,6 +9588,7 @@
/**
* Like grantUriPermissionUncheckedLocked, but takes an Intent.
*/
+ @GuardedBy("this")
void grantUriPermissionUncheckedFromIntentLocked(NeededUriGrants needed,
UriPermissionOwner owner) {
if (needed != null) {
@@ -9525,6 +9600,7 @@
}
}
+ @GuardedBy("this")
void grantUriPermissionFromIntentLocked(int callingUid,
String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg,
@@ -9569,6 +9645,7 @@
}
}
+ @GuardedBy("this")
void removeUriPermissionIfNeededLocked(UriPermission perm) {
if (perm.modeFlags == 0) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
@@ -9585,6 +9662,7 @@
}
}
+ @GuardedBy("this")
private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri,
final int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
@@ -9719,6 +9797,7 @@
* @param targetOnly When {@code true}, only remove permissions where the app is the target,
* not source.
*/
+ @GuardedBy("this")
private void removeUriPermissionsForPackageLocked(
String packageName, int userHandle, boolean persistable, boolean targetOnly) {
if (userHandle == UserHandle.USER_ALL && packageName == null) {
@@ -9905,6 +9984,7 @@
}
}
+ @GuardedBy("this")
private void readGrantedUriPermissionsLocked() {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "readGrantedUriPermissions()");
@@ -10067,6 +10147,7 @@
*
* @return if any mutations occured that require persisting.
*/
+ @GuardedBy("this")
private boolean maybePrunePersistedUriGrantsLocked(int uid) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
if (perms == null) return false;
@@ -11276,8 +11357,11 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- startLockTaskModeLocked(mStackSupervisor.anyTaskForIdLocked(taskId),
- true /* isSystemCaller */);
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+
+ // When starting lock task mode the stack must be in front and focused
+ task.getStack().moveToFront("startSystemLockTaskMode");
+ startLockTaskModeLocked(task, true /* isSystemCaller */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -12419,6 +12503,7 @@
mConstants.start(mContext.getContentResolver());
mCoreSettingsObserver = new CoreSettingsObserver(this);
mFontScaleSettingObserver = new FontScaleSettingObserver();
+ mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
// Now that the settings provider is published we can consider sending
@@ -12563,6 +12648,7 @@
// GLOBAL MANAGEMENT
// =========================================================
+ @GuardedBy("this")
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
@@ -12607,6 +12693,9 @@
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+ // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+ r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
r.persistent = true;
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
@@ -12649,6 +12738,7 @@
}
}
+ @GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
ProcessRecord app;
@@ -12765,6 +12855,7 @@
}
}
+ @GuardedBy("this")
void finishRunningVoiceLocked() {
if (mRunningVoice != null) {
mRunningVoice = null;
@@ -12780,6 +12871,7 @@
}
}
+ @GuardedBy("this")
void updateSleepIfNeededLocked() {
final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
final boolean wasSleeping = mSleeping;
@@ -12844,7 +12936,8 @@
}
// TODO: Where should the corresponding '1' (start) write go?
- StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0);
+ StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
+ StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
boolean timedout = false;
@@ -12883,6 +12976,7 @@
Binder.restoreCallingIdentity(origId);
}
+ @GuardedBy("this")
void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
Slog.d(TAG, "<<< startRunningVoiceLocked()");
mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
@@ -13632,6 +13726,7 @@
// Timed out.
return;
}
+
if ((sendReceiver=pae.receiver) != null) {
// Caller wants result sent back to them.
sendBundle = new Bundle();
@@ -14434,9 +14529,6 @@
mTestPssMode, isSleepingLocked(), now);
}
}
-
- mHandler.removeMessages(REQUEST_ALL_PSS_MSG);
- mHandler.sendEmptyMessageDelayed(REQUEST_ALL_PSS_MSG, 2*60*1000);
}
}
@@ -14743,8 +14835,6 @@
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
BinderInternal.nSetBinderProxyCountEnabled(true);
- //STOPSHIP: Temporary BinderProxy Threshold for b/71353150
- BinderInternal.nSetBinderProxyCountWatermarks(1500, 1200);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@Override
@@ -15447,6 +15537,7 @@
boolean dumpClient = false;
boolean dumpCheckin = false;
boolean dumpCheckinFormat = false;
+ boolean dumpNormalPriority = false;
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
String dumpPackage = null;
@@ -15479,6 +15570,8 @@
dumpCheckin = dumpCheckinFormat = true;
} else if ("-C".equals(opt)) {
dumpCheckinFormat = true;
+ } else if ("--normal-priority".equals(opt)) {
+ dumpNormalPriority = true;
} else if ("-h".equals(opt)) {
ActivityManagerShellCommand.dumpHelp(pw, true);
return;
@@ -15880,11 +15973,15 @@
pw.println("-------------------------------------------------------------------------------");
}
dumpActivityContainersLocked(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
+ // Activities section is dumped as part of the Critical priority dump. Exclude the
+ // section if priority is Normal.
+ if (!dumpNormalPriority){
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
- dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
if (mAssociations.size() > 0) {
pw.println();
if (dumpAll) {
@@ -16115,6 +16212,7 @@
return false;
}
+ @GuardedBy("this")
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
boolean needSep = false;
@@ -16568,6 +16666,7 @@
pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
}
+ @GuardedBy("this")
void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
@@ -17395,6 +17494,7 @@
}
}
+ @GuardedBy("this")
void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -19467,6 +19567,7 @@
* @return Returns true if the given process has been restarted, so the
* app that was passed in must remain on the process lists.
*/
+ @GuardedBy("this")
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
if (index >= 0) {
@@ -19476,6 +19577,7 @@
mProcessesToGc.remove(app);
mPendingPssProcesses.remove(app);
+ ProcessList.abortNextPssTime(app.procStateMemTracker);
// Dismiss any open dialogs.
if (app.crashDialog != null && !app.forceCrashReport) {
@@ -20555,6 +20657,7 @@
}
}
+ @GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
@@ -21545,6 +21648,7 @@
}
}
+ @GuardedBy("this")
void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
if (app.instr == null) {
Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
@@ -23240,9 +23344,9 @@
/**
* Schedule PSS collection of a process.
*/
- void requestPssLocked(ProcessRecord proc, int procState) {
+ boolean requestPssLocked(ProcessRecord proc, int procState) {
if (mPendingPssProcesses.contains(proc)) {
- return;
+ return false;
}
if (mPendingPssProcesses.size() == 0) {
mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
@@ -23251,6 +23355,7 @@
proc.pssProcState = procState;
proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
mPendingPssProcesses.add(proc);
+ return true;
}
/**
@@ -23267,6 +23372,9 @@
if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
mLastFullPssTime = now;
mFullPssPending = true;
+ for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
+ ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
+ }
mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
mPendingPssProcesses.clear();
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
@@ -23275,7 +23383,9 @@
|| app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
continue;
}
- if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
+ if (memLowered || (always && now >
+ app.lastStateTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
+ || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
app.pssProcState = app.setProcState;
app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
: ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
@@ -23284,7 +23394,9 @@
mPendingPssProcesses.add(app);
}
}
- mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ if (!mBgHandler.hasMessages(COLLECT_PSS_BG_MSG)) {
+ mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ }
}
public void setTestPssMode(boolean enabled) {
@@ -23642,7 +23754,7 @@
// Experimental code to more aggressively collect pss while
// running test... the problem is that this tends to collect
// the data right when a process is transitioning between process
- // states, which well tend to give noisy data.
+ // states, which will tend to give noisy data.
long start = SystemClock.uptimeMillis();
long startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(app.pid, mTmpLong, null);
@@ -23665,9 +23777,10 @@
if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
&& now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
mTestPssMode)))) {
- requestPssLocked(app, app.setProcState);
- app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
- app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ if (requestPssLocked(app, app.setProcState)) {
+ app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+ app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ }
} else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
"Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
}
@@ -23934,6 +24047,7 @@
return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}
+ @GuardedBy("this")
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
boolean oomAdj) {
if (isForeground != proc.foregroundServices) {
@@ -24003,6 +24117,7 @@
* if necessary, or skip.
* @return whether updateOomAdjLocked(app) was successful.
*/
+ @GuardedBy("this")
final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
@@ -24027,6 +24142,7 @@
return success;
}
+ @GuardedBy("this")
final void updateOomAdjLocked() {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
@@ -24747,6 +24863,7 @@
/**
* Whitelists {@code targetUid} to temporarily bypass Power Save mode.
*/
+ @GuardedBy("this")
void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
long duration, String tag) {
if (DEBUG_WHITELISTS) {
@@ -24779,6 +24896,7 @@
/**
* Whitelists {@code targetUid} to temporarily bypass Power Save mode.
*/
+ @GuardedBy("this")
void tempWhitelistUidLocked(int targetUid, long duration, String tag) {
mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
setUidTempWhitelistStateLocked(targetUid, true);
@@ -24818,6 +24936,7 @@
}
}
+ @GuardedBy("this")
final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
boolean changed = false;
for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -24832,6 +24951,7 @@
}
}
+ @GuardedBy("this")
final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
boolean changed = false;
final UidRecord uidRec = mActiveUids.get(uid);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f0c90e0..24a77c7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -115,6 +115,7 @@
private int mActivityType;
private int mTaskId;
private boolean mIsTaskOverlay;
+ private boolean mIsLockTask;
final boolean mDumping;
@@ -278,6 +279,7 @@
mActivityType = ACTIVITY_TYPE_UNDEFINED;
mTaskId = INVALID_TASK_ID;
mIsTaskOverlay = false;
+ mIsLockTask = false;
return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@Override
@@ -334,6 +336,8 @@
mTaskId = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--task-overlay")) {
mIsTaskOverlay = true;
+ } else if (opt.equals("--lock-task")) {
+ mIsLockTask = true;
} else {
return false;
}
@@ -429,13 +433,22 @@
options.setLaunchActivityType(mActivityType);
}
if (mTaskId != INVALID_TASK_ID) {
- options = ActivityOptions.makeBasic();
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
options.setLaunchTaskId(mTaskId);
if (mIsTaskOverlay) {
options.setTaskOverlay(true, true /* canResume */);
}
}
+ android.util.Log.d("bfranz", "I was here: " + mIsLockTask);
+ if (mIsLockTask) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLockTaskMode(true);
+ }
if (mWaitOption) {
result = mInterface.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index cae0d2b..4bb18e2 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -133,11 +133,16 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.MoveToDisplayItem;
import android.app.servertransaction.MultiWindowModeChangeItem;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PipModeChangeItem;
+import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.content.ComponentName;
@@ -2370,6 +2375,15 @@
setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+ if (state == INITIALIZING) {
+ // No need to relaunch or schedule new config for activity that hasn't been launched
+ // yet. We do, however, return after applying the config to activity record, so that
+ // it will use it for launch transaction.
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+ "Skipping config check for initializing activity: " + this);
+ return true;
+ }
+
if (changes == 0 && !forceNewConfig) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration no differences in " + this);
@@ -2559,12 +2573,23 @@
+ " callers=" + Debug.getCallers(6));
forceNewConfig = false;
mStackSupervisor.activityRelaunchingLocked(this);
- app.thread.scheduleRelaunchActivity(appToken, pendingResults, pendingNewIntents,
- configChangeFlags, !andResume,
- new Configuration(service.getGlobalConfiguration()),
- new Configuration(getMergedOverrideConfiguration()), preserveWindow);
+ final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
+ pendingNewIntents, configChangeFlags,
+ new MergedConfiguration(service.getGlobalConfiguration(),
+ getMergedOverrideConfiguration()),
+ preserveWindow);
+ final ActivityLifecycleItem lifecycleItem;
+ if (andResume) {
+ lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
+ } else {
+ lifecycleItem = PauseActivityItem.obtain();
+ }
+ final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
+ transaction.addCallback(callbackItem);
+ transaction.setLifecycleStateRequest(lifecycleItem);
+ service.mLifecycleManager.scheduleTransaction(transaction);
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
- // pass in 'andResume' if this activity is currently resumed, which implies we aren't
+ // request resume if this activity is currently resumed, which implies we aren't
// sleeping.
} catch (RemoteException e) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ab2dc36..f3deb8d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -138,6 +138,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
@@ -2211,6 +2212,7 @@
* Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the
* right activity for the current system state.
*/
+ @GuardedBy("mService")
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
@@ -2250,6 +2252,7 @@
mStackSupervisor.mRecentTasks.add(r.getTask());
}
+ @GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
if (!mService.mBooting && !mService.mBooted) {
// Not ready yet!
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index bf38825..831f89a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
@@ -165,6 +166,7 @@
import android.view.Display;
import android.view.RemoteAnimationAdapter;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -1133,19 +1135,31 @@
}
}
- void reportTaskToFrontNoLaunch(ActivityRecord r) {
+ void reportWaitingActivityLaunchedIfNeeded(ActivityRecord r, int result) {
+ if (mWaitingActivityLaunched.isEmpty()) {
+ return;
+ }
+
+ if (result != START_DELIVERED_TO_TOP && result != START_TASK_TO_FRONT) {
+ return;
+ }
+
boolean changed = false;
+
for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
WaitResult w = mWaitingActivityLaunched.remove(i);
if (w.who == null) {
changed = true;
- // Set result to START_TASK_TO_FRONT so that startActivityMayWait() knows that
- // the starting activity ends up moving another activity to front, and it should
- // wait for this new activity to become visible instead.
- // Do not modify other fields.
- w.result = START_TASK_TO_FRONT;
+ w.result = result;
+
+ // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
+ // will be no followup launch signals. Assign the result and launched component.
+ if (result == START_DELIVERED_TO_TOP) {
+ w.who = r.realActivity;
+ }
}
}
+
if (changed) {
mService.notifyAll();
}
@@ -1253,7 +1267,7 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
int modifiedFlags = flags
| PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
- if (intent.isBrowsableWebIntent()
+ if (intent.isWebIntent()
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
modifiedFlags |= PackageManager.MATCH_INSTANT;
}
@@ -1495,6 +1509,8 @@
Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
+ // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
+ // so updating the state should be done accordingly.
if (andResume && readyToResume()) {
// As part of the process of launching, ActivityThread also performs
// a resume.
@@ -1845,6 +1861,7 @@
* Called when the frontmost task is idle.
* @return the state of mService.mBooting before this was called.
*/
+ @GuardedBy("mService")
private boolean checkFinishBootingLocked() {
final boolean booting = mService.mBooting;
boolean enableScreen = false;
@@ -1860,6 +1877,7 @@
}
// Checked.
+ @GuardedBy("mService")
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index eab88aa..055b89b 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -790,7 +790,7 @@
// Instead, launch the ephemeral installer. Once the installer is finished, it
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
- if (rInfo != null && rInfo.isInstantAppAvailable) {
+ if (rInfo != null && rInfo.auxiliaryInfo != null) {
intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
callingPackage, verificationBundle, resolvedType, userId);
resolvedType = null;
@@ -879,11 +879,11 @@
}
// We're waiting for an activity launch to finish, but that activity simply
- // brought another activity to front. Let startActivityMayWait() know about
- // this, so it waits for the new activity to become visible instead.
- if (result == START_TASK_TO_FRONT && !mSupervisor.mWaitingActivityLaunched.isEmpty()) {
- mSupervisor.reportTaskToFrontNoLaunch(mStartActivity);
- }
+ // brought another activity to front. We must also handle the case where the task is already
+ // in the front as a result of the trampoline activity being in the same task (it will be
+ // considered focused as the trampoline will be finished). Let startActivityMayWait() know
+ // about this, so it waits for the new activity to become visible instead.
+ mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
ActivityStack startedActivityStack = null;
final ActivityStack currentStack = r.getStack();
@@ -1076,39 +1076,51 @@
if (outResult != null) {
outResult.result = res;
- if (res == ActivityManager.START_SUCCESS) {
- mSupervisor.mWaitingActivityLaunched.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (outResult.result != START_TASK_TO_FRONT
- && !outResult.timeout && outResult.who == null);
- if (outResult.result == START_TASK_TO_FRONT) {
- res = START_TASK_TO_FRONT;
- }
- }
- if (res == START_TASK_TO_FRONT) {
- final ActivityRecord r = outRecord[0];
- // ActivityRecord may represent a different activity, but it should not be in
- // the resumed state.
- if (r.nowVisible && r.state == RESUMED) {
- outResult.timeout = false;
- outResult.who = r.realActivity;
- outResult.totalTime = 0;
- outResult.thisTime = 0;
- } else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mSupervisor.waitActivityVisible(r.realActivity, outResult);
- // Note: the timeout variable is not currently not ever set.
+ final ActivityRecord r = outRecord[0];
+
+ switch(res) {
+ case START_SUCCESS: {
+ mSupervisor.mWaitingActivityLaunched.add(outResult);
do {
try {
mService.wait();
} catch (InterruptedException e) {
}
- } while (!outResult.timeout && outResult.who == null);
+ } while (outResult.result != START_TASK_TO_FRONT
+ && !outResult.timeout && outResult.who == null);
+ if (outResult.result == START_TASK_TO_FRONT) {
+ res = START_TASK_TO_FRONT;
+ }
+ break;
+ }
+ case START_DELIVERED_TO_TOP: {
+ outResult.timeout = false;
+ outResult.who = r.realActivity;
+ outResult.totalTime = 0;
+ outResult.thisTime = 0;
+ break;
+ }
+ case START_TASK_TO_FRONT: {
+ // ActivityRecord may represent a different activity, but it should not be
+ // in the resumed state.
+ if (r.nowVisible && r.state == RESUMED) {
+ outResult.timeout = false;
+ outResult.who = r.realActivity;
+ outResult.totalTime = 0;
+ outResult.thisTime = 0;
+ } else {
+ outResult.thisTime = SystemClock.uptimeMillis();
+ mSupervisor.waitActivityVisible(r.realActivity, outResult);
+ // Note: the timeout variable is not currently not ever set.
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ }
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 927b72c..ef82f36 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -182,6 +182,7 @@
mExecutorService.shutdownNow();
}
+ @GuardedBy("this")
private Future<?> scheduleSyncLocked(String reason, int flags) {
if (mExecutorService.isShutdown()) {
return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
@@ -248,6 +249,7 @@
}
};
+ @GuardedBy("mWorkerLock")
private void updateExternalStatsLocked(final String reason, int updateFlags) {
// We will request data from external processes asynchronously, and wait on a timeout.
SynchronousResultReceiver wifiReceiver = null;
@@ -372,6 +374,7 @@
return null;
}
+ @GuardedBy("mWorkerLock")
private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
final long lastScanMs = mLastInfo.mControllerScanTimeMs;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0d96468..ea52782 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -325,39 +325,38 @@
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
}
}
void noteProcessAnr(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessAnrLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
}
}
void noteProcessFinish(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessFinishLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
}
}
/** @param state Process state from ActivityManager.java. */
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
ActivityManager.processStateAmToProto(state));
@@ -603,7 +602,6 @@
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
mStats.noteScreenStateLocked(state);
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index d6c6f96..71fd71b8 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -41,6 +41,7 @@
// {Settings.Global.SETTING_NAME, "system_property_name"},
{Settings.Global.SYS_VDSO, "sys.vdso"},
{Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
+ {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
};
@@ -92,7 +93,7 @@
}
try {
systemPropertiesSet(key, value);
- } catch (IllegalArgumentException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
}
}
diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index 52eaca1..c5edb26 100644
--- a/services/core/java/com/android/server/am/PersistentConnection.java
+++ b/services/core/java/com/android/server/am/PersistentConnection.java
@@ -220,6 +220,7 @@
}
}
+ @GuardedBy("mLock")
public final void bindInnerLocked(boolean resetBackoff) {
unscheduleRebindLocked();
@@ -260,6 +261,7 @@
}
}
+ @GuardedBy("mLock")
private void cleanUpConnectionLocked() {
mIsConnected = false;
mService = null;
@@ -276,6 +278,7 @@
}
}
+ @GuardedBy("mLock")
private final void unbindLocked() {
unscheduleRebindLocked();
@@ -289,11 +292,13 @@
cleanUpConnectionLocked();
}
+ @GuardedBy("mLock")
void unscheduleRebindLocked() {
injectRemoveCallbacks(mBindForBackoffRunnable);
mRebindScheduled = false;
}
+ @GuardedBy("mLock")
void scheduleRebindLocked() {
unbindLocked();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 08ee237..bf7aef9 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -501,19 +501,19 @@
private static final int PSS_FIRST_CACHED_INTERVAL = 20*1000;
// The amount of time until PSS when an important process stays in the same state.
- private static final int PSS_SAME_PERSISTENT_INTERVAL = 20*60*1000;
+ private static final int PSS_SAME_PERSISTENT_INTERVAL = 10*60*1000;
// The amount of time until PSS when the top process stays in the same state.
- private static final int PSS_SAME_TOP_INTERVAL = 5*60*1000;
+ private static final int PSS_SAME_TOP_INTERVAL = 1*60*1000;
// The amount of time until PSS when an important process stays in the same state.
- private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000;
+ private static final int PSS_SAME_IMPORTANT_INTERVAL = 10*60*1000;
// The amount of time until PSS when a service process stays in the same state.
- private static final int PSS_SAME_SERVICE_INTERVAL = 20*60*1000;
+ private static final int PSS_SAME_SERVICE_INTERVAL = 5*60*1000;
// The amount of time until PSS when a cached process stays in the same state.
- private static final int PSS_SAME_CACHED_INTERVAL = 20*60*1000;
+ private static final int PSS_SAME_CACHED_INTERVAL = 10*60*1000;
// The amount of time until PSS when a persistent process first appears.
private static final int PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL = 1*60*1000;
@@ -622,16 +622,17 @@
public static final class ProcStateMemTracker {
final int[] mHighestMem = new int[PROC_MEM_NUM];
+ final float[] mScalingFactor = new float[PROC_MEM_NUM];
int mTotalHighestMem = PROC_MEM_CACHED;
- float mCurFactor = 1.0f;
int mPendingMemState;
int mPendingHighestMemState;
- boolean mPendingSame;
+ float mPendingScalingFactor;
public ProcStateMemTracker() {
for (int i = PROC_MEM_PERSISTENT; i < PROC_MEM_NUM; i++) {
mHighestMem[i] = PROC_MEM_NUM;
+ mScalingFactor[i] = 1.0f;
}
mPendingMemState = -1;
}
@@ -639,16 +640,22 @@
public void dumpLine(PrintWriter pw) {
pw.print("best=");
pw.print(mTotalHighestMem);
- pw.print(" ");
- pw.print(mCurFactor);
- pw.print("x (");
+ pw.print(" (");
+ boolean needSep = false;
for (int i = 0; i < PROC_MEM_NUM; i++) {
- if (i != 0) {
- pw.print(", ");
+ if (mHighestMem[i] < PROC_MEM_NUM) {
+ if (needSep) {
+ pw.print(", ");
+ needSep = false;
+ }
+ pw.print(i);
+ pw.print("=");
+ pw.print(mHighestMem[i]);
+ pw.print(" ");
+ pw.print(mScalingFactor[i]);
+ pw.print("x");
+ needSep = true;
}
- pw.print(i);
- pw.print("=");
- pw.print(mHighestMem[i]);
}
pw.print(")");
if (mPendingMemState >= 0) {
@@ -656,8 +663,9 @@
pw.print(mPendingMemState);
pw.print(" highest=");
pw.print(mPendingHighestMemState);
- pw.print(" same=");
- pw.print(mPendingSame);
+ pw.print(" ");
+ pw.print(mPendingScalingFactor);
+ pw.print("x");
}
pw.println();
}
@@ -674,12 +682,8 @@
public static void commitNextPssTime(ProcStateMemTracker tracker) {
if (tracker.mPendingMemState >= 0) {
tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
+ tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
- if (tracker.mPendingSame) {
- tracker.mCurFactor *= 1.5f;
- } else {
- tracker.mCurFactor = 1;
- }
tracker.mPendingMemState = -1;
}
}
@@ -691,6 +695,7 @@
public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
boolean sleeping, long now) {
boolean first;
+ float scalingFactor;
final int memState = sProcStateToProcMem[procState];
if (tracker != null) {
final int highestMemState = memState < tracker.mTotalHighestMem
@@ -698,9 +703,15 @@
first = highestMemState < tracker.mHighestMem[memState];
tracker.mPendingMemState = memState;
tracker.mPendingHighestMemState = highestMemState;
- tracker.mPendingSame = !first;
+ if (first) {
+ tracker.mPendingScalingFactor = scalingFactor = 1.0f;
+ } else {
+ scalingFactor = tracker.mScalingFactor[memState];
+ tracker.mPendingScalingFactor = scalingFactor * 1.5f;
+ }
} else {
first = true;
+ scalingFactor = 1.0f;
}
final long[] table = test
? (first
@@ -709,8 +720,7 @@
: (first
? (sleeping ? sFirstAsleepPssTimes : sFirstAwakePssTimes)
: (sleeping ? sSameAsleepPssTimes : sSameAwakePssTimes));
- long delay = (long)(table[memState] * (tracker != null && !first
- ? tracker.mCurFactor : 1.0f));
+ long delay = (long)(table[memState] * scalingFactor);
if (delay > PSS_MAX_INTERVAL) {
delay = PSS_MAX_INTERVAL;
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 8bf320e..c10d81b 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -135,6 +135,7 @@
return mMemFactorLowered;
}
+ @GuardedBy("mAm")
public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
mMemFactorLowered = memFactor < mLastMemOnlyState;
mLastMemOnlyState = memFactor;
@@ -237,6 +238,7 @@
if (commit) {
mProcessStats.resetSafely();
updateFile();
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
}
mLastWriteTime = SystemClock.uptimeMillis();
totalTime = SystemClock.uptimeMillis() - now;
@@ -784,12 +786,14 @@
} else if ("--reset".equals(arg)) {
synchronized (mAm) {
mProcessStats.resetSafely();
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
pw.println("Process stats reset.");
quit = true;
}
} else if ("--clear".equals(arg)) {
synchronized (mAm) {
mProcessStats.resetSafely();
+ mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
ArrayList<String> files = getCommittedFiles(0, true, true);
if (files != null) {
for (int fi=0; fi<files.size(); fi++) {
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index e7b067b..db4e09f 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -27,6 +28,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
+import android.os.Trace;
import android.view.IRecentsAnimationRunner;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
import com.android.server.wm.WindowManagerService;
@@ -70,6 +72,7 @@
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
ComponentName recentsComponent, int recentsUid) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
mWindowManager.deferSurfaceLayout();
try {
// Cancel the previous recents animation if necessary
@@ -124,6 +127,7 @@
mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
} finally {
mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -134,6 +138,8 @@
if (mWindowManager.getRecentsAnimationController() == null) return;
mWindowManager.inSurfaceTransaction(() -> {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
mWindowManager.deferSurfaceLayout();
try {
mWindowManager.cleanupRecentsAnimation();
@@ -167,6 +173,7 @@
mWindowManager.executeAppTransition();
} finally {
mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
});
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 7b0c714..d54e264 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -743,7 +743,7 @@
mInjector.stackSupervisorRemoveUser(userId);
// Remove the user if it is ephemeral.
if (getUserInfo(userId).isEphemeral()) {
- mInjector.getUserManager().removeUser(userId);
+ mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
// Evict the user's credential encryption key.
try {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a0a7af3..e95608f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -23,6 +23,7 @@
import static android.os.Process.FIRST_APPLICATION_UID;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -240,6 +241,7 @@
private static final int MSG_DYN_POLICY_MIX_STATE_UPDATE = 25;
private static final int MSG_INDICATE_SYSTEM_READY = 26;
private static final int MSG_ACCESSORY_PLUG_MEDIA_UNMUTE = 27;
+ private static final int MSG_NOTIFY_VOL_EVENT = 28;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1334,11 +1336,9 @@
extVolCtlr = mExtVolumeController;
}
if (extVolCtlr != null) {
- try {
- mExtVolumeController.notifyVolumeAdjust(direction);
- } catch(RemoteException e) {
- // nothing we can do about this. Do not log error, too much potential for spam
- }
+ sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
+ direction, 0 /*ignored*/,
+ extVolCtlr, 0 /*delay*/);
} else {
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
caller, Binder.getCallingUid());
@@ -5054,6 +5054,15 @@
state);
}
+ private void onNotifyVolumeEvent(@NonNull IAudioPolicyCallback apc,
+ @AudioManager.VolumeAdjustment int direction) {
+ try {
+ apc.notifyVolumeAdjust(direction);
+ } catch(Exception e) {
+ // nothing we can do about this. Do not log error, too much potential for spam
+ }
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -5216,6 +5225,10 @@
case MSG_DYN_POLICY_MIX_STATE_UPDATE:
onDynPolicyMixStateUpdate((String) msg.obj, msg.arg1);
break;
+
+ case MSG_NOTIFY_VOL_EVENT:
+ onNotifyVolumeEvent((IAudioPolicyCallback) msg.obj, msg.arg1);
+ break;
}
}
}
@@ -7296,6 +7309,12 @@
//======================
/** */
public int dispatchFocusChange(AudioFocusInfo afi, int focusChange, IAudioPolicyCallback pcb) {
+ if (afi == null) {
+ throw new IllegalArgumentException("Illegal null AudioFocusInfo");
+ }
+ if (pcb == null) {
+ throw new IllegalArgumentException("Illegal null AudioPolicy callback");
+ }
synchronized (mAudioPolicies) {
if (!mAudioPolicies.containsKey(pcb.asBinder())) {
throw new IllegalStateException("Unregistered AudioPolicy for focus dispatch");
@@ -7304,6 +7323,23 @@
}
}
+ public void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult,
+ IAudioPolicyCallback pcb) {
+ if (afi == null) {
+ throw new IllegalArgumentException("Illegal null AudioFocusInfo");
+ }
+ if (pcb == null) {
+ throw new IllegalArgumentException("Illegal null AudioPolicy callback");
+ }
+ synchronized (mAudioPolicies) {
+ if (!mAudioPolicies.containsKey(pcb.asBinder())) {
+ throw new IllegalStateException("Unregistered AudioPolicy for external focus");
+ }
+ mMediaFocusControl.setFocusRequestResultFromExtPolicy(afi, requestResult);
+ }
+ }
+
+
//======================
// misc
//======================
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index f2ef02f..99f0840 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -241,15 +241,15 @@
void release() {
+ final IBinder srcRef = mSourceRef;
+ final AudioFocusDeathHandler deathHdlr = mDeathHandler;
try {
- if (mSourceRef != null && mDeathHandler != null) {
- mSourceRef.unlinkToDeath(mDeathHandler, 0);
- mDeathHandler = null;
- mFocusDispatcher = null;
+ if (srcRef != null && deathHdlr != null) {
+ srcRef.unlinkToDeath(deathHdlr, 0);
}
- } catch (java.util.NoSuchElementException e) {
- Log.e(TAG, "FocusRequester.release() hit ", e);
- }
+ } catch (java.util.NoSuchElementException e) { }
+ mDeathHandler = null;
+ mFocusDispatcher = null;
}
@Override
@@ -424,7 +424,7 @@
int dispatchFocusChange(int focusChange) {
if (mFocusDispatcher == null) {
- if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: no focus dispatcher"); }
+ if (MediaFocusControl.DEBUG) { Log.e(TAG, "dispatchFocusChange: no focus dispatcher"); }
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
if (focusChange == AudioManager.AUDIOFOCUS_NONE) {
@@ -445,12 +445,29 @@
try {
mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId);
} catch (android.os.RemoteException e) {
- Log.v(TAG, "dispatchFocusChange: error talking to focus listener", e);
+ Log.e(TAG, "dispatchFocusChange: error talking to focus listener " + mClientId, e);
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
+ void dispatchFocusResultFromExtPolicy(int requestResult) {
+ if (mFocusDispatcher == null) {
+ if (MediaFocusControl.DEBUG) {
+ Log.e(TAG, "dispatchFocusResultFromExtPolicy: no focus dispatcher");
+ }
+ }
+ if (DEBUG) {
+ Log.v(TAG, "dispatching result" + requestResult + " to " + mClientId);
+ }
+ try {
+ mFocusDispatcher.dispatchFocusResultFromExtPolicy(requestResult, mClientId);
+ } catch (android.os.RemoteException e) {
+ Log.e(TAG, "dispatchFocusResultFromExtPolicy: error talking to focus listener"
+ + mClientId, e);
+ }
+ }
+
AudioFocusInfo toAudioFocusInfo() {
return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
mFocusGainRequest, mFocusLossReceived, mGrantFlags, mSdkTarget);
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 9ddc52a..d023bd7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -83,6 +83,10 @@
private boolean mRingOrCallActive = false;
+ private final Object mExtFocusChangeLock = new Object();
+ @GuardedBy("mExtFocusChangeLock")
+ private long mExtFocusChangeCounter;
+
protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
mContext = cntxt;
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -521,7 +525,7 @@
* @param requestResult
* @return true if the external audio focus policy (if any) is handling the focus request
*/
- boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult,
+ boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
IAudioFocusDispatcher fd, IBinder cb) {
if (mFocusPolicy == null) {
return false;
@@ -530,6 +534,9 @@
Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
+ " dispatcher=" + fd);
}
+ synchronized (mExtFocusChangeLock) {
+ afi.setGen(mExtFocusChangeCounter++);
+ }
final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
if (existingFr != null) {
if (!existingFr.hasSameDispatcher(fd)) {
@@ -538,8 +545,7 @@
mFocusOwnersForFocusPolicy.put(afi.getClientId(),
new FocusRequester(afi, fd, cb, hdlr, this));
}
- } else if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
- || requestResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
+ } else {
// new focus (future) focus owner to keep track of
final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
mFocusOwnersForFocusPolicy.put(afi.getClientId(),
@@ -547,12 +553,25 @@
}
try {
//oneway
- mFocusPolicy.notifyAudioFocusRequest(afi, requestResult);
+ mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+ return true;
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
+ mFocusPolicy.asBinder(), e);
}
- return true;
+ return false;
+ }
+
+ void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) {
+ synchronized (mExtFocusChangeLock) {
+ if (afi.getGen() > mExtFocusChangeCounter) {
+ return;
+ }
+ }
+ final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
+ if (fr != null) {
+ fr.dispatchFocusResultFromExtPolicy(requestResult);
+ }
}
/**
@@ -590,7 +609,12 @@
if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); }
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
- final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
+ final FocusRequester fr;
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+ fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
+ } else {
+ fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
+ }
if (fr == null) {
if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); }
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
@@ -710,9 +734,7 @@
boolean focusGrantDelayed = false;
if (!canReassignAudioFocus()) {
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
- final int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, result, fd, cb);
- return result;
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
} else {
// request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
// granted right now, so the requester will be inserted in the focus stack
@@ -721,12 +743,11 @@
}
}
- // external focus policy: delay request for focus gain?
- final int resultWithExtPolicy = AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
+ // external focus policy?
if (notifyExtFocusPolicyFocusRequest_syncAf(
- afiForExtPolicy, resultWithExtPolicy, fd, cb)) {
+ afiForExtPolicy, fd, cb)) {
// stop handling focus request here as it is handled by external audio focus policy
- return resultWithExtPolicy;
+ return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
}
// handle the potential premature death of the new holder of the focus
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 3bb3d1f..5af19ec 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -21,6 +21,7 @@
import android.hardware.broadcastradio.V2_0.AmFmBandRange;
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
import android.hardware.broadcastradio.V2_0.Announcement;
+import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IdentifierType;
import android.hardware.broadcastradio.V2_0.ProgramFilter;
import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
@@ -196,9 +197,15 @@
return bands.toArray(new RadioManager.BandDescriptor[bands.size()]);
}
+ private static @Nullable Map<String, Integer> dabConfigFromHal(
+ @Nullable List<DabTableEntry> config) {
+ if (config == null) return null;
+ return config.stream().collect(Collectors.toMap(e -> e.label, e -> e.frequency));
+ }
+
static @NonNull RadioManager.ModuleProperties
propertiesFromHal(int id, @NonNull String serviceName, @NonNull Properties prop,
- @Nullable AmFmRegionConfig amfmConfig) {
+ @Nullable AmFmRegionConfig amfmConfig, @Nullable List<DabTableEntry> dabConfig) {
Objects.requireNonNull(serviceName);
Objects.requireNonNull(prop);
@@ -228,6 +235,7 @@
true, // isBgScanSupported is deprecated
supportedProgramTypes,
supportedIdentifierTypes,
+ dabConfigFromHal(dabConfig),
vendorInfoFromHal(prop.vendorInfo)
);
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 50f032d..daec97a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -24,6 +24,7 @@
import android.hardware.radio.RadioManager;
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
import android.hardware.broadcastradio.V2_0.Announcement;
+import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IAnnouncementListener;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.broadcastradio.V2_0.ICloseHandle;
@@ -58,12 +59,17 @@
if (service == null) return null;
Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
- service.getAmFmRegionConfig(false, (int result, AmFmRegionConfig config) -> {
+ service.getAmFmRegionConfig(false, (result, config) -> {
if (result == Result.OK) amfmConfig.value = config;
});
- RadioManager.ModuleProperties prop =
- Convert.propertiesFromHal(idx, fqName, service.getProperties(), amfmConfig.value);
+ Mutable<List<DabTableEntry>> dabConfig = new Mutable<>();
+ service.getDabRegionConfig((result, config) -> {
+ if (result == Result.OK) dabConfig.value = config;
+ });
+
+ RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
+ service.getProperties(), amfmConfig.value, dabConfig.value);
return new RadioModule(service, prop);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/car/CarServiceHelperService.java b/services/core/java/com/android/server/car/CarServiceHelperService.java
deleted file mode 100644
index 9392a6a..0000000
--- a/services/core/java/com/android/server/car/CarServiceHelperService.java
+++ /dev/null
@@ -1,91 +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 com.android.server.car;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.car.ICarServiceHelper;
-import com.android.server.SystemService;
-
-/**
- * System service side companion service for CarService.
- * Starts car service and provide necessary API for CarService. Only for car product.
- */
-public class CarServiceHelperService extends SystemService {
- private static final String TAG = "CarServiceHelper";
- private static final String CAR_SERVICE_INTERFACE = "android.car.ICar";
- private final ICarServiceHelperImpl mHelper = new ICarServiceHelperImpl();
- private IBinder mCarService;
- private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
- Slog.i(TAG, "**CarService connected**");
- mCarService = iBinder;
- // Cannot depend on ICar which is defined in CarService, so handle binder call directly
- // instead.
- // void setCarServiceHelper(in IBinder helper)
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(CAR_SERVICE_INTERFACE);
- data.writeStrongBinder(mHelper.asBinder());
- try {
- mCarService.transact(IBinder.FIRST_CALL_TRANSACTION, // setCarServiceHelper
- data, null, Binder.FLAG_ONEWAY);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException from car service", e);
- handleCarServiceCrash();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- handleCarServiceCrash();
- }
- };
-
- public CarServiceHelperService(Context context) {
- super(context);
- }
-
- @Override
- public void onStart() {
- Intent intent = new Intent();
- intent.setPackage("com.android.car");
- intent.setAction(CAR_SERVICE_INTERFACE);
- if (!getContext().bindServiceAsUser(intent, mCarServiceConnection, Context.BIND_AUTO_CREATE,
- UserHandle.SYSTEM)) {
- Slog.wtf(TAG, "cannot start car service");
- }
- }
-
- private void handleCarServiceCrash() {
- //TODO define recovery bahavior
- }
-
- private class ICarServiceHelperImpl extends ICarServiceHelper.Stub {
- //TODO
- }
-}
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index 6f77e04..ce50558 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 6280edb..c3f020a 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -48,8 +48,8 @@
import android.os.FactoryTest;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -446,7 +446,7 @@
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
- uri.getAuthority());
+ uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid));
}
}
@@ -502,6 +502,9 @@
int userId = UserHandle.getCallingUserId();
int uId = Binder.getCallingUid();
+ validateExtras(uId, extras);
+ final boolean isForegroundSyncRequest = isForegroundSyncRequest(uId, extras);
+
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
@@ -509,7 +512,8 @@
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleSync(account, userId, uId, authority, extras,
- SyncStorageEngine.AuthorityInfo.UNDEFINED);
+ SyncStorageEngine.AuthorityInfo.UNDEFINED,
+ /*isAppStandbyExempted=*/ isForegroundSyncRequest);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -548,6 +552,12 @@
public void syncAsUser(SyncRequest request, int userId) {
enforceCrossUserPermission(userId, "no permission to request sync as user: " + userId);
int callerUid = Binder.getCallingUid();
+
+ final Bundle extras = request.getBundle();
+
+ validateExtras(callerUid, extras);
+ final boolean isForegroundSyncRequest = isForegroundSyncRequest(callerUid, extras);
+
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
@@ -556,8 +566,6 @@
if (syncManager == null) {
return;
}
-
- Bundle extras = request.getBundle();
long flextime = request.getSyncFlexTime();
long runAtTime = request.getSyncRunTime();
if (request.isPeriodic()) {
@@ -575,7 +583,8 @@
} else {
syncManager.scheduleSync(
request.getAccount(), userId, callerUid, request.getProvider(), extras,
- SyncStorageEngine.AuthorityInfo.UNDEFINED);
+ SyncStorageEngine.AuthorityInfo.UNDEFINED,
+ /*isAppStandbyExempted=*/ isForegroundSyncRequest);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -649,10 +658,13 @@
"no permission to write the sync settings");
}
+ Bundle extras = new Bundle(request.getBundle());
+ validateExtras(callingUid, extras);
+
long identityToken = clearCallingIdentity();
try {
SyncStorageEngine.EndPoint info;
- Bundle extras = new Bundle(request.getBundle());
+
Account account = request.getAccount();
String provider = request.getProvider();
info = new SyncStorageEngine.EndPoint(account, provider, userId);
@@ -787,6 +799,8 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ validateExtras(Binder.getCallingUid(), extras);
+
int userId = UserHandle.getCallingUserId();
pollFrequency = clampPeriod(pollFrequency);
@@ -815,6 +829,8 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ validateExtras(Binder.getCallingUid(), extras);
+
final int callingUid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -1100,6 +1116,7 @@
return (pi != null) ? pi.packageName : null;
}
+ @GuardedBy("mCache")
private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
String providerPackageName) {
ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
@@ -1115,6 +1132,7 @@
return packageCache;
}
+ @GuardedBy("mCache")
private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
if (userCache == null) return;
@@ -1239,6 +1257,56 @@
return SyncStorageEngine.AuthorityInfo.UNDEFINED;
}
+ private void validateExtras(int callingUid, Bundle extras) {
+ if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
+ || extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
+ ) {
+ switch (callingUid) {
+ case Process.ROOT_UID:
+ case Process.SHELL_UID:
+ case Process.SYSTEM_UID:
+ break; // Okay
+ default:
+ throw new SecurityException("Invalid extras specified.");
+ }
+ }
+ }
+
+ private boolean isForegroundSyncRequest(int callingUid, Bundle extras) {
+ final boolean isForegroundRequest;
+ if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)) {
+ isForegroundRequest = true;
+ } else if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC)) {
+ isForegroundRequest = false;
+ } else {
+ isForegroundRequest = isUidInForeground(callingUid);
+ }
+ extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC);
+ extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC);
+
+ return isForegroundRequest;
+ }
+
+ private boolean isUidInForeground(int uid) {
+ // If the caller is ADB, we assume it's a background request by default, because
+ // that's also the default of requests from the requestsync command.
+ // The requestsync command will always set either SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC or
+ // SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC (for non-periodic sync requests),
+ // so it shouldn't matter in practice.
+ switch (uid) {
+ case Process.SHELL_UID:
+ case Process.ROOT_UID:
+ return false;
+ }
+ final ActivityManagerInternal ami =
+ LocalServices.getService(ActivityManagerInternal.class);
+ if (ami != null) {
+ return ami.getUidProcessState(uid)
+ <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
+ return false;
+ }
+
/**
* Hide this class since it is not part of api,
* but current unittest framework requires it to be public
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 51499f7..9980930 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -137,11 +137,13 @@
+ " params=" + jobParametersToString(params));
}
} else if (runtime < 10 * 1000) {
- // Job stopped too soon. WTF.
- wtf("Job " + jobId + " stopped in " + runtime + " ms: "
- + " startUptime=" + startUptime
- + " nowUptime=" + nowUptime
- + " params=" + jobParametersToString(params));
+ // This happens too in a normal case too, and it's rather too often.
+ // Disable it for now.
+// // Job stopped too soon. WTF.
+// wtf("Job " + jobId + " stopped in " + runtime + " ms: "
+// + " startUptime=" + startUptime
+// + " nowUptime=" + nowUptime
+// + " params=" + jobParametersToString(params));
}
mStartedSyncs.delete(jobId);
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 75c0181..20aec7e 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -194,6 +194,7 @@
}
}
+ @GuardedBy("mLock")
private void openLogLocked(long now) {
// If we already have a log file opened and the date has't changed, just use it.
final long day = now % DateUtils.DAY_IN_MILLIS;
@@ -219,6 +220,7 @@
}
}
+ @GuardedBy("mLock")
private void closeCurrentLogLocked() {
IoUtils.closeQuietly(mLogWriter);
mLogWriter = null;
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 422d0cb..de17ec7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -576,9 +576,10 @@
mSyncStorageEngine = SyncStorageEngine.getSingleton();
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
@Override
- public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
+ public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras,
+ boolean isAppStandbyExempted) {
scheduleSync(info.account, info.userId, reason, info.provider, extras,
- AuthorityInfo.UNDEFINED);
+ AuthorityInfo.UNDEFINED, isAppStandbyExempted);
}
});
@@ -608,7 +609,8 @@
if (!removed) {
scheduleSync(null, UserHandle.USER_ALL,
SyncOperation.REASON_SERVICE_CHANGED,
- type.authority, null, AuthorityInfo.UNDEFINED);
+ type.authority, null, AuthorityInfo.UNDEFINED,
+ /*isAppStandbyExempted=*/ false);
}
}
}, mSyncHandler);
@@ -656,7 +658,8 @@
if (mAccountManagerInternal.hasAccountAccess(account, uid)) {
scheduleSync(account, UserHandle.getUserId(uid),
SyncOperation.REASON_ACCOUNTS_UPDATED,
- null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS);
+ null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS,
+ /*isAppStandbyExempted=*/ false);
}
});
@@ -881,17 +884,19 @@
* Use {@link AuthorityInfo#UNDEFINED} to sync all authorities.
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, int targetSyncState) {
+ String requestedAuthority, Bundle extras, int targetSyncState,
+ boolean isAppStandbyExempted) {
scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
- 0 /* min delay */, true /* checkIfAccountReady */);
+ 0 /* min delay */, true /* checkIfAccountReady */, isAppStandbyExempted);
}
/**
* @param minDelayMillis The sync can't land before this delay expires.
*/
private void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, int targetSyncState,
- final long minDelayMillis, boolean checkIfAccountReady) {
+ String requestedAuthority, Bundle extras, int targetSyncState,
+ final long minDelayMillis, boolean checkIfAccountReady,
+ boolean isAppStandbyExempted) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -1009,7 +1014,8 @@
&& result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
scheduleSync(account.account, userId, reason, authority,
finalExtras, targetSyncState, minDelayMillis,
- true /* checkIfAccountReady */);
+ true /* checkIfAccountReady */,
+ isAppStandbyExempted);
}
}
));
@@ -1060,7 +1066,7 @@
sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
() -> scheduleSync(account.account, account.userId, reason,
authority, finalExtras, targetSyncState, minDelayMillis,
- false));
+ false, isAppStandbyExempted));
} else {
// Initialisation sync.
Bundle newExtras = new Bundle();
@@ -1078,7 +1084,8 @@
postScheduleSyncMessage(
new SyncOperation(account.account, account.userId,
owningUid, owningPackage, reason, source,
- authority, newExtras, allowParallelSyncs),
+ authority, newExtras, allowParallelSyncs,
+ isAppStandbyExempted),
minDelayMillis
);
}
@@ -1095,7 +1102,7 @@
postScheduleSyncMessage(
new SyncOperation(account.account, account.userId,
owningUid, owningPackage, reason, source,
- authority, extras, allowParallelSyncs),
+ authority, extras, allowParallelSyncs, isAppStandbyExempted),
minDelayMillis
);
}
@@ -1208,11 +1215,13 @@
* Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY
* ms to batch syncs.
*/
- public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
+ public void scheduleLocalSync(Account account, int userId, int reason, String authority,
+ boolean isAppStandbyExempted) {
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(account, userId, reason, authority, extras,
- AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */);
+ AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */,
+ isAppStandbyExempted);
}
public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1480,7 +1489,11 @@
}
// Check if duplicate syncs are pending. If found, keep one with least expected run time.
+
+ // If any of the duplicate ones has exemption, then we inherit it.
if (!syncOperation.isPeriodic) {
+ boolean inheritAppStandbyExemption = false;
+
// Check currently running syncs
for (ActiveSyncContext asc: mActiveSyncContexts) {
if (asc.mSyncOperation.key.equals(syncOperation.key)) {
@@ -1496,14 +1509,14 @@
long now = SystemClock.elapsedRealtime();
syncOperation.expectedRuntime = now + minDelay;
List<SyncOperation> pending = getAllPendingSyncs();
- SyncOperation opWithLeastExpectedRuntime = syncOperation;
+ SyncOperation syncToRun = syncOperation;
for (SyncOperation op : pending) {
if (op.isPeriodic) {
continue;
}
if (op.key.equals(syncOperation.key)) {
- if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) {
- opWithLeastExpectedRuntime = op;
+ if (syncToRun.expectedRuntime > op.expectedRuntime) {
+ syncToRun = op;
}
duplicatesCount++;
}
@@ -1511,26 +1524,54 @@
if (duplicatesCount > 1) {
Slog.e(TAG, "FATAL ERROR! File a bug if you see this.");
}
+
+ if (syncOperation != syncToRun) {
+ // If there's a duplicate with an earlier run time that's not exempted,
+ // and if the current operation is exempted with no minDelay,
+ // cancel the duplicate one and keep the current one.
+ //
+ // This means the duplicate one has a negative expected run time, but it hasn't
+ // been executed possibly because of app-standby.
+
+ if (syncOperation.isAppStandbyExempted
+ && (minDelay == 0)
+ && !syncToRun.isAppStandbyExempted) {
+ syncToRun = syncOperation;
+ }
+ }
+
+ // Cancel all other duplicate syncs.
for (SyncOperation op : pending) {
if (op.isPeriodic) {
continue;
}
if (op.key.equals(syncOperation.key)) {
- if (op != opWithLeastExpectedRuntime) {
+ if (op != syncToRun) {
if (isLoggable) {
Slog.v(TAG, "Cancelling duplicate sync " + op);
}
+ if (op.isAppStandbyExempted) {
+ inheritAppStandbyExemption = true;
+ }
cancelJob(op, "scheduleSyncOperationH-duplicate");
}
}
}
- if (opWithLeastExpectedRuntime != syncOperation) {
+ if (syncToRun != syncOperation) {
// Don't schedule because a duplicate sync with earlier expected runtime exists.
if (isLoggable) {
Slog.v(TAG, "Not scheduling because a duplicate exists.");
}
+
+ // TODO Should we give the winning one SYNC_EXTRAS_APP_STANDBY_EXEMPTED
+ // if the current one has it?
return;
}
+
+ // If any of the duplicates had exemption, we exempt the current one.
+ if (inheritAppStandbyExemption) {
+ syncOperation.isAppStandbyExempted = true;
+ }
}
// Syncs that are re-scheduled shouldn't get a new job id.
@@ -1547,12 +1588,18 @@
final int networkType = syncOperation.isNotAllowedOnMetered() ?
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
+ // Note this logic means when an exempted sync fails,
+ // the back-off one will inherit it too, and will be exempted from app-standby.
+ final int jobFlags = syncOperation.isAppStandbyExempted
+ ? JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY : 0;
+
JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
new ComponentName(mContext, SyncJobService.class))
.setExtras(syncOperation.toJobInfoExtras())
.setRequiredNetworkType(networkType)
.setPersisted(true)
- .setPriority(priority);
+ .setPriority(priority)
+ .setFlags(jobFlags);
if (syncOperation.isPeriodic) {
b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis);
@@ -1683,12 +1730,12 @@
EndPoint target = new EndPoint(null, null, userId);
updateRunningAccounts(target);
- // Schedule sync for any accounts under started user.
+ // Schedule sync for any accounts under started user, but only the NOT_INITIALIZED adapters.
final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
mContext.getOpPackageName());
for (Account account : accounts) {
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
- AuthorityInfo.NOT_INITIALIZED);
+ AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false);
}
}
@@ -3144,7 +3191,8 @@
if (syncTargets != null) {
scheduleSync(syncTargets.account, syncTargets.userId,
SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
- null, AuthorityInfo.NOT_INITIALIZED);
+ null, AuthorityInfo.NOT_INITIALIZED,
+ /*isAppStandbyExempted=*/ false);
}
}
@@ -3211,7 +3259,7 @@
syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC, extras,
syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
- pollFrequencyMillis, flexMillis);
+ pollFrequencyMillis, flexMillis, /*isAppStandbyExempted=*/ false);
final int syncOpState = computeSyncOpState(op);
switch (syncOpState) {
@@ -3590,7 +3638,8 @@
syncOperation.owningUid, syncOperation.owningPackage,
syncOperation.reason,
syncOperation.syncSource, info.provider, new Bundle(),
- syncOperation.allowParallelSyncs));
+ syncOperation.allowParallelSyncs,
+ syncOperation.isAppStandbyExempted));
}
}
@@ -3808,6 +3857,10 @@
if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
return true;
}
+// if (key.equals(ContentResolver.SYNC_EXTRAS_APP_STANDBY_EXEMPTED)) {
+// return true;
+// }
+ // No need to check virtual flags such as SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC.
return false;
}
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 7d2cc00..f6b4819 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -98,29 +98,33 @@
/** jobId of the JobScheduler job corresponding to this sync */
public int jobId;
+ /** Whether this operation should be exempted from the app-standby throttling. */
+ public boolean isAppStandbyExempted;
+
public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
int reason, int source, String provider, Bundle extras,
- boolean allowParallelSyncs) {
+ boolean allowParallelSyncs, boolean isAppStandbyExempted) {
this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
- reason, source, extras, allowParallelSyncs);
+ reason, source, extras, allowParallelSyncs, isAppStandbyExempted);
}
private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
- int reason, int source, Bundle extras, boolean allowParallelSyncs) {
+ int reason, int source, Bundle extras, boolean allowParallelSyncs,
+ boolean isAppStandbyExempted) {
this(info, owningUid, owningPackage, reason, source, extras, allowParallelSyncs, false,
- NO_JOB_ID, 0, 0);
+ NO_JOB_ID, 0, 0, isAppStandbyExempted);
}
public SyncOperation(SyncOperation op, long periodMillis, long flexMillis) {
this(op.target, op.owningUid, op.owningPackage, op.reason, op.syncSource,
new Bundle(op.extras), op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
- periodMillis, flexMillis);
+ periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
}
public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
int reason, int source, Bundle extras, boolean allowParallelSyncs,
boolean isPeriodic, int sourcePeriodicId, long periodMillis,
- long flexMillis) {
+ long flexMillis, boolean isAppStandbyExempted) {
this.target = info;
this.owningUid = owningUid;
this.owningPackage = owningPackage;
@@ -134,6 +138,7 @@
this.flexMillis = flexMillis;
this.jobId = NO_JOB_ID;
this.key = toKey();
+ this.isAppStandbyExempted = isAppStandbyExempted;
}
/* Get a one off sync operation instance from a periodic sync. */
@@ -143,7 +148,7 @@
}
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */,
- periodMillis, flexMillis);
+ periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
return op;
}
@@ -161,6 +166,7 @@
periodMillis = other.periodMillis;
flexMillis = other.flexMillis;
this.key = other.key;
+ isAppStandbyExempted = other.isAppStandbyExempted;
}
/**
@@ -229,6 +235,7 @@
jobInfoExtras.putLong("flexMillis", flexMillis);
jobInfoExtras.putLong("expectedRuntime", expectedRuntime);
jobInfoExtras.putInt("retries", retries);
+ jobInfoExtras.putBoolean("isAppStandbyExempted", isAppStandbyExempted);
return jobInfoExtras;
}
@@ -249,6 +256,7 @@
Bundle extras;
boolean allowParallelSyncs, isPeriodic;
long periodMillis, flexMillis;
+ boolean isAppStandbyExempted;
if (!jobExtras.getBoolean("SyncManagerJob", false)) {
return null;
@@ -267,6 +275,7 @@
initiatedBy = jobExtras.getInt("sourcePeriodicId", NO_JOB_ID);
periodMillis = jobExtras.getLong("periodMillis");
flexMillis = jobExtras.getLong("flexMillis");
+ isAppStandbyExempted = jobExtras.getBoolean("isAppStandbyExempted", false);
extras = new Bundle();
PersistableBundle syncExtras = jobExtras.getPersistableBundle("syncExtras");
@@ -288,7 +297,8 @@
SyncStorageEngine.EndPoint target =
new SyncStorageEngine.EndPoint(account, provider, userId);
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, source,
- extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis);
+ extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis,
+ isAppStandbyExempted);
op.jobId = jobExtras.getInt("jobId");
op.expectedRuntime = jobExtras.getLong("expectedRuntime");
op.retries = jobExtras.getInt("retries");
@@ -375,6 +385,9 @@
if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
sb.append(" EXPEDITED");
}
+ if (isAppStandbyExempted) {
+ sb.append(" STANDBY-EXEMPTED");
+ }
sb.append(" Reason=");
sb.append(reasonToString(pm, reason));
if (isPeriodic) {
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 5a37ee2..8b67b7a 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -340,7 +340,8 @@
interface OnSyncRequestListener {
/** Called when a sync is needed on an account(s) due to some change in state. */
- public void onSyncRequest(EndPoint info, int reason, Bundle extras);
+ public void onSyncRequest(EndPoint info, int reason, Bundle extras,
+ boolean exemptFromAppStandby);
}
interface PeriodicSyncAddedListener {
@@ -675,7 +676,8 @@
if (sync) {
requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName,
- new Bundle());
+ new Bundle(),
+ /* exemptFromAppStandby=*/ false);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
queueBackup();
@@ -736,7 +738,8 @@
writeAccountInfoLocked();
}
if (syncable == AuthorityInfo.SYNCABLE) {
- requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle());
+ requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
+ /*exemptFromAppStandby=*/ false); // Or the caller FG state?
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
@@ -908,7 +911,8 @@
}
if (flag) {
requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
- new Bundle());
+ new Bundle(),
+ /*exemptFromAppStandby=*/ false); // Or the caller FG state?
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
@@ -2138,10 +2142,12 @@
}
}
- private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras) {
+ private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras,
+ boolean exemptFromAppStandby) {
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
&& mSyncRequestListener != null) {
- mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras);
+ mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras,
+ exemptFromAppStandby);
} else {
SyncRequest.Builder req =
new SyncRequest.Builder()
@@ -2153,7 +2159,7 @@
}
private void requestSync(Account account, int userId, int reason, String authority,
- Bundle extras) {
+ Bundle extras, boolean exemptFromAppStandby) {
// If this is happening in the system process, then call the syncrequest listener
// to make a request back to the SyncManager directly.
// If this is probably a test instance, then call back through the ContentResolver
@@ -2162,8 +2168,7 @@
&& mSyncRequestListener != null) {
mSyncRequestListener.onSyncRequest(
new EndPoint(account, authority, userId),
- reason,
- extras);
+ reason, extras, exemptFromAppStandby);
} else {
ContentResolver.requestSync(account, authority, extras);
}
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
new file mode 100644
index 0000000..6e571bd
--- /dev/null
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -0,0 +1,363 @@
+/*
+ * 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 com.android.server.display;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class that stores stats of ambient brightness regions as histogram.
+ */
+public class AmbientBrightnessStatsTracker {
+
+ private static final String TAG = "AmbientBrightnessStatsTracker";
+ private static final boolean DEBUG = false;
+
+ @VisibleForTesting
+ static final float[] BUCKET_BOUNDARIES_FOR_NEW_STATS =
+ {0, 0.1f, 0.3f, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000};
+ @VisibleForTesting
+ static final int MAX_DAYS_TO_TRACK = 7;
+
+ private final AmbientBrightnessStats mAmbientBrightnessStats;
+ private final Timer mTimer;
+ private final Injector mInjector;
+ private final UserManager mUserManager;
+ private float mCurrentAmbientBrightness;
+ private @UserIdInt int mCurrentUserId;
+
+ public AmbientBrightnessStatsTracker(UserManager userManager, @Nullable Injector injector) {
+ mUserManager = userManager;
+ if (injector != null) {
+ mInjector = injector;
+ } else {
+ mInjector = new Injector();
+ }
+ mAmbientBrightnessStats = new AmbientBrightnessStats();
+ mTimer = new Timer(() -> mInjector.elapsedRealtimeMillis());
+ mCurrentAmbientBrightness = -1;
+ }
+
+ public synchronized void start() {
+ mTimer.reset();
+ mTimer.start();
+ }
+
+ public synchronized void stop() {
+ if (mTimer.isRunning()) {
+ mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
+ mCurrentAmbientBrightness, mTimer.totalDurationSec());
+ }
+ mTimer.reset();
+ mCurrentAmbientBrightness = -1;
+ }
+
+ public synchronized void add(@UserIdInt int userId, float newAmbientBrightness) {
+ if (mTimer.isRunning()) {
+ if (userId == mCurrentUserId) {
+ mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
+ mCurrentAmbientBrightness, mTimer.totalDurationSec());
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "User switched since last sensor event.");
+ }
+ mCurrentUserId = userId;
+ }
+ mTimer.reset();
+ mTimer.start();
+ mCurrentAmbientBrightness = newAmbientBrightness;
+ } else {
+ if (DEBUG) {
+ Slog.e(TAG, "Timer not running while trying to add brightness stats.");
+ }
+ }
+ }
+
+ public synchronized void writeStats(OutputStream stream) throws IOException {
+ mAmbientBrightnessStats.writeToXML(stream);
+ }
+
+ public synchronized void readStats(InputStream stream) throws IOException {
+ mAmbientBrightnessStats.readFromXML(stream);
+ }
+
+ public synchronized ArrayList<AmbientBrightnessDayStats> getUserStats(int userId) {
+ return mAmbientBrightnessStats.getUserStats(userId);
+ }
+
+ public synchronized void dump(PrintWriter pw) {
+ pw.println("AmbientBrightnessStats:");
+ pw.print(mAmbientBrightnessStats);
+ }
+
+ /**
+ * AmbientBrightnessStats tracks ambient brightness stats across users over multiple days.
+ * This class is not ThreadSafe.
+ */
+ class AmbientBrightnessStats {
+
+ private static final String TAG_AMBIENT_BRIGHTNESS_STATS = "ambient-brightness-stats";
+ private static final String TAG_AMBIENT_BRIGHTNESS_DAY_STATS =
+ "ambient-brightness-day-stats";
+ private static final String ATTR_USER = "user";
+ private static final String ATTR_LOCAL_DATE = "local-date";
+ private static final String ATTR_BUCKET_BOUNDARIES = "bucket-boundaries";
+ private static final String ATTR_BUCKET_STATS = "bucket-stats";
+
+ private Map<Integer, Deque<AmbientBrightnessDayStats>> mStats;
+
+ public AmbientBrightnessStats() {
+ mStats = new HashMap<>();
+ }
+
+ public void log(@UserIdInt int userId, LocalDate localDate, float ambientBrightness,
+ float durationSec) {
+ Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(mStats, userId);
+ AmbientBrightnessDayStats dayStats = getOrCreateDayStats(userStats, localDate);
+ dayStats.log(ambientBrightness, durationSec);
+ }
+
+ public ArrayList<AmbientBrightnessDayStats> getUserStats(@UserIdInt int userId) {
+ if (mStats.containsKey(userId)) {
+ return new ArrayList<>(mStats.get(userId));
+ } else {
+ return null;
+ }
+ }
+
+ public void writeToXML(OutputStream stream) throws IOException {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
+ out.startTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
+ for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
+ for (AmbientBrightnessDayStats userDayStats : entry.getValue()) {
+ int userSerialNumber = mInjector.getUserSerialNumber(mUserManager,
+ entry.getKey());
+ if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) {
+ out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
+ out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber));
+ out.attribute(null, ATTR_LOCAL_DATE,
+ userDayStats.getLocalDate().toString());
+ StringBuilder bucketBoundariesValues = new StringBuilder();
+ StringBuilder timeSpentValues = new StringBuilder();
+ for (int i = 0; i < userDayStats.getBucketBoundaries().length; i++) {
+ if (i > 0) {
+ bucketBoundariesValues.append(",");
+ timeSpentValues.append(",");
+ }
+ bucketBoundariesValues.append(userDayStats.getBucketBoundaries()[i]);
+ timeSpentValues.append(userDayStats.getStats()[i]);
+ }
+ out.attribute(null, ATTR_BUCKET_BOUNDARIES,
+ bucketBoundariesValues.toString());
+ out.attribute(null, ATTR_BUCKET_STATS, timeSpentValues.toString());
+ out.endTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
+ }
+ }
+ }
+ out.endTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
+ out.endDocument();
+ stream.flush();
+ }
+
+ public void readFromXML(InputStream stream) throws IOException {
+ try {
+ Map<Integer, Deque<AmbientBrightnessDayStats>> parsedStats = new HashMap<>();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+ String tag = parser.getName();
+ if (!TAG_AMBIENT_BRIGHTNESS_STATS.equals(tag)) {
+ throw new XmlPullParserException(
+ "Ambient brightness stats not found in tracker file " + tag);
+ }
+
+ final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
+ parser.next();
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ tag = parser.getName();
+ if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) {
+ String userSerialNumber = parser.getAttributeValue(null, ATTR_USER);
+ LocalDate localDate = LocalDate.parse(
+ parser.getAttributeValue(null, ATTR_LOCAL_DATE));
+ String[] bucketBoundaries = parser.getAttributeValue(null,
+ ATTR_BUCKET_BOUNDARIES).split(",");
+ String[] bucketStats = parser.getAttributeValue(null,
+ ATTR_BUCKET_STATS).split(",");
+ if (bucketBoundaries.length != bucketStats.length
+ || bucketBoundaries.length < 1) {
+ throw new IOException("Invalid brightness stats string.");
+ }
+ float[] parsedBucketBoundaries = new float[bucketBoundaries.length];
+ float[] parsedBucketStats = new float[bucketStats.length];
+ for (int i = 0; i < bucketBoundaries.length; i++) {
+ parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]);
+ parsedBucketStats[i] = Float.parseFloat(bucketStats[i]);
+ }
+ int userId = mInjector.getUserId(mUserManager,
+ Integer.parseInt(userSerialNumber));
+ if (userId != -1 && localDate.isAfter(cutOffDate)) {
+ Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(
+ parsedStats, userId);
+ userStats.offer(
+ new AmbientBrightnessDayStats(localDate,
+ parsedBucketBoundaries, parsedBucketStats));
+ }
+ }
+ }
+ mStats = parsedStats;
+ } catch (NullPointerException | NumberFormatException | XmlPullParserException |
+ DateTimeParseException | IOException e) {
+ throw new IOException("Failed to parse brightness stats file.", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
+ for (AmbientBrightnessDayStats dayStats : entry.getValue()) {
+ builder.append(" ");
+ builder.append(entry.getKey()).append(" ");
+ builder.append(dayStats).append("\n");
+ }
+ }
+ return builder.toString();
+ }
+
+ private Deque<AmbientBrightnessDayStats> getOrCreateUserStats(
+ Map<Integer, Deque<AmbientBrightnessDayStats>> stats, @UserIdInt int userId) {
+ if (!stats.containsKey(userId)) {
+ stats.put(userId, new ArrayDeque<>());
+ }
+ return stats.get(userId);
+ }
+
+ private AmbientBrightnessDayStats getOrCreateDayStats(
+ Deque<AmbientBrightnessDayStats> userStats, LocalDate localDate) {
+ AmbientBrightnessDayStats lastBrightnessStats = userStats.peekLast();
+ if (lastBrightnessStats != null && lastBrightnessStats.getLocalDate().equals(
+ localDate)) {
+ return lastBrightnessStats;
+ } else {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(localDate,
+ BUCKET_BOUNDARIES_FOR_NEW_STATS);
+ if (userStats.size() == MAX_DAYS_TO_TRACK) {
+ userStats.poll();
+ }
+ userStats.offer(dayStats);
+ return dayStats;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ interface Clock {
+ long elapsedTimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Timer {
+
+ private final Clock clock;
+ private long startTimeMillis;
+ private boolean started;
+
+ public Timer(Clock clock) {
+ this.clock = clock;
+ }
+
+ public void reset() {
+ started = false;
+ }
+
+ public void start() {
+ if (!started) {
+ startTimeMillis = clock.elapsedTimeMillis();
+ started = true;
+ }
+ }
+
+ public boolean isRunning() {
+ return started;
+ }
+
+ public float totalDurationSec() {
+ if (started) {
+ return (float) ((clock.elapsedTimeMillis() - startTimeMillis) / 1000.0);
+ }
+ return 0;
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ public long elapsedRealtimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ public int getUserSerialNumber(UserManager userManager, int userId) {
+ return userManager.getUserSerialNumber(userId);
+ }
+
+ public int getUserId(UserManager userManager, int userSerialNumber) {
+ return userManager.getUserHandle(userSerialNumber);
+ }
+
+ public LocalDate getLocalDate() {
+ return LocalDate.now();
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6a88b1e..e2aee88 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -269,6 +269,14 @@
}
}
+ public boolean hasUserDataPoints() {
+ return mBrightnessMapper.hasUserDataPoints();
+ }
+
+ public boolean isDefaultConfig() {
+ return mBrightnessMapper.isDefaultConfig();
+ }
+
private boolean setDisplayPolicy(int policy) {
if (mDisplayPolicy == policy) {
return false;
diff --git a/services/core/java/com/android/server/display/BrightnessIdleJob.java b/services/core/java/com/android/server/display/BrightnessIdleJob.java
index 876acf4..b0a41cb 100644
--- a/services/core/java/com/android/server/display/BrightnessIdleJob.java
+++ b/services/core/java/com/android/server/display/BrightnessIdleJob.java
@@ -70,7 +70,7 @@
Slog.d(BrightnessTracker.TAG, "Scheduled write of brightness events");
}
DisplayManagerInternal dmi = LocalServices.getService(DisplayManagerInternal.class);
- dmi.persistBrightnessSliderEvents();
+ dmi.persistBrightnessTrackerState();
return false;
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 001d4d9..c0d2599 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -200,6 +200,12 @@
*/
public abstract void clearUserDataPoints();
+ /** @return true if there are any short term adjustments applied to the curve */
+ public abstract boolean hasUserDataPoints();
+
+ /** @return true if the current brightness config is the default one */
+ public abstract boolean isDefaultConfig();
+
public abstract void dump(PrintWriter pw);
private static float normalizeAbsoluteBrightness(int brightness) {
@@ -390,6 +396,16 @@
}
@Override
+ public boolean hasUserDataPoints() {
+ return mUserLux != -1;
+ }
+
+ @Override
+ public boolean isDefaultConfig() {
+ return true;
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("SimpleMappingStrategy");
pw.println(" mSpline=" + mSpline);
@@ -507,6 +523,16 @@
}
@Override
+ public boolean hasUserDataPoints() {
+ return mUserLux != -1;
+ }
+
+ @Override
+ public boolean isDefaultConfig() {
+ return mDefaultConfig.equals(mConfig);
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("PhysicalMappingStrategy");
pw.println(" mConfig=" + mConfig);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index bcf8bfe..6b4666a 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -28,8 +29,8 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
-import android.net.Uri;
import android.os.BatteryManager;
import android.os.Environment;
import android.os.Handler;
@@ -81,6 +82,7 @@
static final boolean DEBUG = false;
private static final String EVENTS_FILE = "brightness_events.xml";
+ private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
private static final int MAX_EVENTS = 100;
// Discard events when reading or writing that are older than this.
private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
@@ -99,6 +101,9 @@
private static final String ATTR_NIGHT_MODE = "nightMode";
private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
private static final String ATTR_LAST_NITS = "lastNits";
+ private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
+ private static final String ATTR_POWER_SAVE = "powerSaveFactor";
+ private static final String ATTR_USER_POINT = "userPoint";
private static final int MSG_BACKGROUND_START = 0;
private static final int MSG_BRIGHTNESS_CHANGED = 1;
@@ -113,6 +118,10 @@
private final Runnable mEventsWriter = () -> writeEvents();
private volatile boolean mWriteEventsScheduled;
+ private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
+ private final Runnable mAmbientBrightnessStatsWriter = () -> writeAmbientBrightnessStats();
+ private volatile boolean mWriteBrightnessStatsScheduled;
+
private UserManager mUserManager;
private final Context mContext;
private final ContentResolver mContentResolver;
@@ -120,6 +129,7 @@
// mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread.
private BroadcastReceiver mBroadcastReceiver;
private SensorListener mSensorListener;
+ private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
// Lock held while collecting data related to brightness changes.
private final Object mDataCollectionLock = new Object();
@@ -157,12 +167,19 @@
}
mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
mUserManager = mContext.getSystemService(UserManager.class);
-
+ try {
+ final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
+ mCurrentUserId = focusedStack.userId;
+ } catch (RemoteException e) {
+ // Really shouldn't be possible.
+ return;
+ }
mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
}
private void backgroundStart(float initialBrightness) {
readEvents();
+ readAmbientBrightnessStats();
mSensorListener = new SensorListener();
@@ -196,12 +213,20 @@
mInjector.unregisterSensorListener(mContext, mSensorListener);
mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
mInjector.cancelIdleJob(mContext);
+ mAmbientBrightnessStatsTracker.stop();
synchronized (mDataCollectionLock) {
mStarted = false;
}
}
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
+ }
+ mCurrentUserId = newUserId;
+ }
+
/**
* @param userId userId to fetch data for.
* @param includePackage if false we will null out BrightnessChangeEvent.packageName
@@ -228,24 +253,29 @@
return new ParceledListSlice<>(out);
}
- public void persistEvents() {
- scheduleWriteEvents();
+ public void persistBrightnessTrackerState() {
+ scheduleWriteBrightnessTrackerState();
}
/**
* Notify the BrightnessTracker that the user has changed the brightness of the display.
*/
- public void notifyBrightnessChanged(float brightness, boolean userInitiated) {
+ public void notifyBrightnessChanged(float brightness, boolean userInitiated,
+ float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig) {
if (DEBUG) {
Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
brightness, userInitiated));
}
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
- userInitiated ? 1 : 0, 0 /*unused*/, (Float) brightness);
+ userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
+ powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig));
m.sendToTarget();
}
- private void handleBrightnessChanged(float brightness, boolean userInitiated) {
+ private void handleBrightnessChanged(float brightness, boolean userInitiated,
+ float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig) {
BrightnessChangeEvent.Builder builder;
synchronized (mDataCollectionLock) {
@@ -267,6 +297,9 @@
builder = new BrightnessChangeEvent.Builder();
builder.setBrightness(brightness);
builder.setTimeStamp(mInjector.currentTimeMillis());
+ builder.setPowerBrightnessFactor(powerBrightnessFactor);
+ builder.setUserBrightnessPoint(isUserSetBrightness);
+ builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
final int readingCount = mLastSensorReadings.size();
if (readingCount == 0) {
@@ -321,11 +354,15 @@
}
}
- private void scheduleWriteEvents() {
+ private void scheduleWriteBrightnessTrackerState() {
if (!mWriteEventsScheduled) {
mBgHandler.post(mEventsWriter);
mWriteEventsScheduled = true;
}
+ if (!mWriteBrightnessStatsScheduled) {
+ mBgHandler.post(mAmbientBrightnessStatsWriter);
+ mWriteBrightnessStatsScheduled = true;
+ }
}
private void writeEvents() {
@@ -336,7 +373,7 @@
return;
}
- final AtomicFile writeTo = mInjector.getFile();
+ final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
if (writeTo == null) {
return;
}
@@ -360,12 +397,29 @@
}
}
+ private void writeAmbientBrightnessStats() {
+ mWriteBrightnessStatsScheduled = false;
+ final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
+ if (writeTo == null) {
+ return;
+ }
+ FileOutputStream output = null;
+ try {
+ output = writeTo.startWrite();
+ mAmbientBrightnessStatsTracker.writeStats(output);
+ writeTo.finishWrite(output);
+ } catch (IOException e) {
+ writeTo.failWrite(output);
+ Slog.e(TAG, "Failed to write ambient brightness stats.", e);
+ }
+ }
+
private void readEvents() {
synchronized (mEventsLock) {
// Read might prune events so mark as dirty.
mEventsDirty = true;
mEvents.clear();
- final AtomicFile readFrom = mInjector.getFile();
+ final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
if (readFrom != null && readFrom.exists()) {
FileInputStream input = null;
try {
@@ -381,6 +435,23 @@
}
}
+ private void readAmbientBrightnessStats() {
+ mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
+ final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
+ if (readFrom != null && readFrom.exists()) {
+ FileInputStream input = null;
+ try {
+ input = readFrom.openRead();
+ mAmbientBrightnessStatsTracker.readStats(input);
+ } catch (IOException e) {
+ readFrom.delete();
+ Slog.e(TAG, "Failed to read ambient brightness stats.", e);
+ } finally {
+ IoUtils.closeQuietly(input);
+ }
+ }
+ }
+
@VisibleForTesting
@GuardedBy("mEventsLock")
void writeEventsLocked(OutputStream stream) throws IOException {
@@ -412,6 +483,12 @@
toWrite[i].colorTemperature));
out.attribute(null, ATTR_LAST_NITS,
Float.toString(toWrite[i].lastBrightness));
+ out.attribute(null, ATTR_DEFAULT_CONFIG,
+ Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
+ out.attribute(null, ATTR_POWER_SAVE,
+ Float.toString(toWrite[i].powerBrightnessFactor));
+ out.attribute(null, ATTR_USER_POINT,
+ Boolean.toString(toWrite[i].isUserSetBrightness));
StringBuilder luxValues = new StringBuilder();
StringBuilder luxTimestamps = new StringBuilder();
for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
@@ -496,6 +573,21 @@
builder.setLuxValues(luxValues);
builder.setLuxTimestamps(luxTimestamps);
+ String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
+ if (defaultConfig != null) {
+ builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
+ }
+ String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
+ if (powerSave != null) {
+ builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
+ } else {
+ builder.setPowerBrightnessFactor(1.0f);
+ }
+ String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
+ if (userPoint != null) {
+ builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
+ }
+
BrightnessChangeEvent event = builder.build();
if (DEBUG) {
Slog.i(TAG, "Read event " + event.brightness
@@ -535,7 +627,11 @@
BrightnessChangeEvent[] events = mEvents.toArray();
for (int i = 0; i < events.length; ++i) {
pw.print(" " + events[i].timeStamp + ", " + events[i].userId);
- pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness + ", {");
+ pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
+ pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
+ pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
+ pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig);
+ pw.print(" {");
for (int j = 0; j < events[i].luxValues.length; ++j){
if (j != 0) {
pw.print(", ");
@@ -545,6 +641,13 @@
pw.println("}");
}
}
+ if (mAmbientBrightnessStatsTracker != null) {
+ mAmbientBrightnessStatsTracker.dump(pw);
+ }
+ }
+
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
+ return new ParceledListSlice<>(mAmbientBrightnessStatsTracker.getUserStats(userId));
}
// Not allowed to keep the SensorEvent so used to copy the data we care about.
@@ -584,6 +687,10 @@
}
}
+ private void recordAmbientBrightnessStats(SensorEvent event) {
+ mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
+ }
+
private void batteryLevelChanged(int level, int scale) {
synchronized (mDataCollectionLock) {
mLastBatteryLevel = (float) level / (float) scale;
@@ -594,6 +701,7 @@
@Override
public void onSensorChanged(SensorEvent event) {
recordSensorEvent(event);
+ recordAmbientBrightnessStats(event);
}
@Override
@@ -611,7 +719,7 @@
String action = intent.getAction();
if (Intent.ACTION_SHUTDOWN.equals(action)) {
stop();
- scheduleWriteEvents();
+ scheduleWriteBrightnessTrackerState();
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
@@ -619,8 +727,10 @@
batteryLevelChanged(level, scale);
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mAmbientBrightnessStatsTracker.stop();
mInjector.unregisterSensorListener(mContext, mSensorListener);
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ mAmbientBrightnessStatsTracker.start();
mInjector.registerSensorListener(mContext, mSensorListener,
mInjector.getBackgroundHandler());
}
@@ -637,14 +747,31 @@
backgroundStart((float)msg.obj /*initial brightness*/);
break;
case MSG_BRIGHTNESS_CHANGED:
- float newBrightness = (float) msg.obj;
+ BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
boolean userInitiatedChange = (msg.arg1 == 1);
- handleBrightnessChanged(newBrightness, userInitiatedChange);
+ handleBrightnessChanged(values.brightness, userInitiatedChange,
+ values.powerBrightnessFactor, values.isUserSetBrightness,
+ values.isDefaultBrightnessConfig);
break;
}
}
}
+ private static class BrightnessChangeValues {
+ final float brightness;
+ final float powerBrightnessFactor;
+ final boolean isUserSetBrightness;
+ final boolean isDefaultBrightnessConfig;
+
+ BrightnessChangeValues(float brightness, float powerBrightnessFactor,
+ boolean isUserSetBrightness, boolean isDefaultBrightnessConfig) {
+ this.brightness = brightness;
+ this.powerBrightnessFactor = powerBrightnessFactor;
+ this.isUserSetBrightness = isUserSetBrightness;
+ this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+ }
+ }
+
@VisibleForTesting
static class Injector {
public void registerSensorListener(Context context,
@@ -679,8 +806,8 @@
return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
}
- public AtomicFile getFile() {
- return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
+ public AtomicFile getFile(String filename) {
+ return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
}
public long currentTimeMillis() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0c2ff05..a5c1fe2 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -38,6 +38,7 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerGlobal;
@@ -359,6 +360,7 @@
mPersistentDataStore.getBrightnessConfiguration(userSerial);
mDisplayPowerController.setBrightnessConfiguration(config);
}
+ mDisplayPowerController.onSwitchUser(newUserId);
}
}
@@ -1835,6 +1837,23 @@
}
@Override // Binder call
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS,
+ "Permission required to to access ambient light stats.");
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ return mDisplayPowerController.getAmbientBrightnessStats(userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void setBrightnessConfigurationForUser(
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
mContext.enforceCallingOrSelfPermission(
@@ -2039,9 +2058,9 @@
}
@Override
- public void persistBrightnessSliderEvents() {
+ public void persistBrightnessTrackerState() {
synchronized (mSyncRoot) {
- mDisplayPowerController.persistBrightnessSliderEvents();
+ mDisplayPowerController.persistBrightnessTrackerState();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 80aec42..b27f1ec 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -34,6 +34,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
@@ -478,6 +479,7 @@
mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
mTemporaryScreenBrightness = -1;
+ mPendingScreenBrightnessSetting = -1;
mTemporaryAutoBrightnessAdjustment = Float.NaN;
}
@@ -498,11 +500,20 @@
return mBrightnessTracker.getEvents(userId, includePackage);
}
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ mBrightnessTracker.onSwitchUser(newUserId);
+ }
+
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
+ @UserIdInt int userId) {
+ return mBrightnessTracker.getAmbientBrightnessStats(userId);
+ }
+
/**
- * Persist the brightness slider events to disk.
+ * Persist the brightness slider events and ambient brightness stats to disk.
*/
- public void persistBrightnessSliderEvents() {
- mBrightnessTracker.persistEvents();
+ public void persistBrightnessTrackerState() {
+ mBrightnessTracker.persistBrightnessTrackerState();
}
/**
@@ -795,13 +806,15 @@
brightness = PowerManager.BRIGHTNESS_ON;
}
- // If the brightness is already set then it's been overriden by something other than the
+ // If the brightness is already set then it's been overridden by something other than the
// user, or is a temporary adjustment.
final boolean userInitiatedChange = brightness < 0
&& (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
+ boolean hadUserBrightnessPoint = false;
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
+ hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
mAutomaticBrightnessController.configure(autoBrightnessEnabled,
mBrightnessConfiguration,
mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
@@ -930,7 +943,7 @@
}
if (!brightnessIsTemporary) {
- notifyBrightnessChanged(brightness, userInitiatedChange);
+ notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint);
}
}
@@ -1464,18 +1477,26 @@
mPendingScreenBrightnessSetting = -1;
return false;
}
+ mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
mPendingScreenBrightnessSetting = -1;
return true;
}
- private void notifyBrightnessChanged(int brightness, boolean userInitiated) {
+ private void notifyBrightnessChanged(int brightness, boolean userInitiated,
+ boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
- if (brightnessInNits >= 0.0f) {
+ if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
+ && mAutomaticBrightnessController != null) {
// We only want to track changes on devices that can actually map the display backlight
// values into a physical brightness unit since the value provided by the API is in
// nits and not using the arbitrary backlight units.
- mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated);
+ final float powerFactor = mPowerRequest.lowPowerMode
+ ? mPowerRequest.screenLowPowerBrightnessFactor
+ : 1.0f;
+ mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+ powerFactor, hadUserDataPoint,
+ mAutomaticBrightnessController.isDefaultConfig());
}
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
index 0976a22..b0cde2d 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
@@ -202,6 +202,7 @@
}
}
+ @GuardedBy("this")
private void readStateSyncLocked() {
FileInputStream in;
if (!mFile.exists()) {
@@ -226,6 +227,7 @@
}
}
+ @GuardedBy("this")
private void parseStateLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
@@ -243,6 +245,7 @@
}
}
+ @GuardedBy("this")
private void parseFingerprintsLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 1e09383..de0f298 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1299,6 +1299,7 @@
/**
* Return external input devices.
*/
+ @GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
return mSafeExternalInputs;
}
@@ -1421,6 +1422,7 @@
}
}
+ @GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3d079cc..b06dba9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1097,6 +1097,7 @@
}
}
+ @GuardedBy("mLock")
private List<HdmiDeviceInfo> getMhlDevicesLocked() {
return mMhlDevices;
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e0baeee..401c05e 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -862,7 +862,8 @@
}
startTrackingJobLocked(jobStatus, toCancel);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uId, null, jobStatus.getBatteryName(), 2);
+ uId, null, jobStatus.getBatteryName(),
+ StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 37b3990..4988974 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -295,10 +295,12 @@
}
/** Called externally when a job that was scheduled for execution should be cancelled. */
+ @GuardedBy("mLock")
void cancelExecutingJobLocked(int reason, String debugReason) {
doCancelLocked(reason, debugReason);
}
+ @GuardedBy("mLock")
void preemptExecutingJobLocked() {
doCancelLocked(JobParameters.REASON_PREEMPT, "cancelled due to preemption");
}
@@ -319,6 +321,7 @@
return mTimeoutElapsed;
}
+ @GuardedBy("mLock")
boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId,
String reason) {
final JobStatus executing = getRunningJobLocked();
@@ -512,6 +515,7 @@
}
}
+ @GuardedBy("mLock")
void doServiceBoundLocked() {
removeOpTimeOutLocked();
handleServiceBoundLocked();
@@ -531,6 +535,7 @@
}
}
+ @GuardedBy("mLock")
void doCallbackLocked(boolean reschedule, String reason) {
if (DEBUG) {
Slog.d(TAG, "doCallback of : " + mRunningJob
@@ -550,6 +555,7 @@
}
}
+ @GuardedBy("mLock")
void doCancelLocked(int arg1, String debugReason) {
if (mVerb == VERB_FINISHED) {
if (DEBUG) {
@@ -567,6 +573,7 @@
}
/** Start the job on the service. */
+ @GuardedBy("mLock")
private void handleServiceBoundLocked() {
if (DEBUG) {
Slog.d(TAG, "handleServiceBound for " + getRunningJobNameLocked());
@@ -605,6 +612,7 @@
* _EXECUTING -> Error
* _STOPPING -> Error
*/
+ @GuardedBy("mLock")
private void handleStartedLocked(boolean workOngoing) {
switch (mVerb) {
case VERB_STARTING:
@@ -637,6 +645,7 @@
* _STARTING -> Error
* _PENDING -> Error
*/
+ @GuardedBy("mLock")
private void handleFinishedLocked(boolean reschedule, String reason) {
switch (mVerb) {
case VERB_EXECUTING:
@@ -659,6 +668,7 @@
* in the message queue.
* _ENDING -> No point in doing anything here, so we ignore.
*/
+ @GuardedBy("mLock")
private void handleCancelLocked(String reason) {
if (JobSchedulerService.DEBUG) {
Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
@@ -683,6 +693,7 @@
}
/** Process MSG_TIMEOUT here. */
+ @GuardedBy("mLock")
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
@@ -722,6 +733,7 @@
* Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
* VERB_STOPPING.
*/
+ @GuardedBy("mLock")
private void sendStopMessageLocked(String reason) {
removeOpTimeOutLocked();
if (mVerb != VERB_EXECUTING) {
@@ -747,6 +759,7 @@
* or from acknowledging the stop message we sent. Either way, we're done tracking it and
* we want to clean up internally.
*/
+ @GuardedBy("mLock")
private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
final JobStatus completedJob;
if (mVerb == VERB_FINISHED) {
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 2e4567a..5eb7700 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -92,7 +92,7 @@
jobStatus.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, uid);
- pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
+ pw.print(mForceAppStandbyTracker.isUidActive(uid) ? " active" : " idle");
if (mForceAppStandbyTracker.isUidPowerSaveWhitelisted(uid) ||
mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(uid)) {
pw.print(", whitelisted");
@@ -136,7 +136,7 @@
proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
proto.write(TrackedJob.IS_IN_FOREGROUND,
- mForceAppStandbyTracker.isInForeground(sourceUid));
+ mForceAppStandbyTracker.isUidActive(sourceUid));
proto.write(TrackedJob.IS_WHITELISTED,
mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 373d87d..77e813e 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -92,6 +92,7 @@
mNetPolicyManager.registerListener(mNetPolicyListener);
}
+ @GuardedBy("mLock")
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.hasConnectivityConstraint()) {
@@ -101,6 +102,7 @@
}
}
+ @GuardedBy("mLock")
@Override
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
@@ -325,13 +327,16 @@
}
};
+ @GuardedBy("mLock")
@Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.print("Connectivity: connected=");
- pw.print(mConnected);
+ pw.println(mConnected);
+
pw.print("Tracking ");
pw.print(mTrackedJobs.size());
- pw.println(":");
+ pw.println(" jobs");
+
for (int i = 0; i < mTrackedJobs.size(); i++) {
final JobStatus js = mTrackedJobs.valueAt(i);
if (js.shouldDump(filterUid)) {
@@ -339,11 +344,14 @@
js.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
- pw.print(": "); pw.print(js.getJob().getRequiredNetwork());
+ pw.print(": ");
+ pw.print(js.getJob().getRequiredNetwork());
+ pw.println();
}
}
}
+ @GuardedBy("mLock")
@Override
public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
final long token = proto.start(fieldId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 28fa86b..6deff36 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -383,8 +383,8 @@
return KeyStore.getInstance();
}
- public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
- return RecoverableKeyStoreManager.getInstance(mContext);
+ public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
+ return RecoverableKeyStoreManager.getInstance(mContext, keyStore);
}
public IStorageManager getStorageManager() {
@@ -413,7 +413,7 @@
mInjector = injector;
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
- mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
+ mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
mHandler = injector.getHandler();
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
@@ -1887,7 +1887,7 @@
mSpManager.removeUser(userId);
mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
- cleanSpCache();
+ tryRemoveUserFromSpCacheLater(userId);
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -2064,6 +2064,16 @@
return mRecoverableKeyStoreManager.generateAndStoreKey(alias);
}
+ @Override
+ public String generateKey(@NonNull String alias, byte[] account) throws RemoteException {
+ return mRecoverableKeyStoreManager.generateKey(alias, account);
+ }
+
+ @Override
+ public String getKey(@NonNull String alias) throws RemoteException {
+ return mRecoverableKeyStoreManager.getKey(alias);
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
@@ -2141,6 +2151,13 @@
private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+ // Preemptively cache the SP and then try to remove it in a handler.
+ Slog.i(TAG, "Caching SP for user " + userId);
+ synchronized (mSpManager) {
+ mSpCache.put(userId, auth);
+ }
+ tryRemoveUserFromSpCacheLater(userId);
+
// Pass the primary user's auth secret to the HAL
if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
try {
@@ -2154,33 +2171,25 @@
Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e);
}
}
-
- // Update the SP cache, removing the entry when allowed
- synchronized (mSpManager) {
- if (shouldCacheSpForUser(userId)) {
- Slog.i(TAG, "Caching SP for user " + userId);
- mSpCache.put(userId, auth);
- } else {
- Slog.i(TAG, "Not caching SP for user " + userId);
- mSpCache.delete(userId);
- }
- }
}
- /** Clean up the SP cache by removing unneeded entries. */
- private void cleanSpCache() {
- synchronized (mSpManager) {
- // Preserve indicies after removal by iterating backwards
- for (int i = mSpCache.size() - 1; i >= 0; --i) {
- final int userId = mSpCache.keyAt(i);
- if (!shouldCacheSpForUser(userId)) {
- Slog.i(TAG, "Uncaching SP for user " + userId);
- mSpCache.removeAt(i);
+ private void tryRemoveUserFromSpCacheLater(@UserIdInt int userId) {
+ mHandler.post(() -> {
+ if (!shouldCacheSpForUser(userId)) {
+ // The transition from 'should not cache' to 'should cache' can only happen if
+ // certain admin apps are installed after provisioning e.g. via adb. This is not
+ // a common case and we do not seamlessly support; it may result in the SP not
+ // being cached when it is needed. The cache can be re-populated by verifying
+ // the credential again.
+ Slog.i(TAG, "Removing SP from cache for user " + userId);
+ synchronized (mSpManager) {
+ mSpCache.remove(userId);
}
}
- }
+ });
}
+ /** Do not hold any of the locks from this service when calling. */
private boolean shouldCacheSpForUser(@UserIdInt int userId) {
// Before the user setup has completed, an admin could be installed that requires the SP to
// be cached (see below).
@@ -2227,9 +2236,10 @@
* This happens during a normal migration case when the user currently has password.
*
* 2. credentialhash == null and credential == null
- * A new SP blob and a new SID will be created, while the user has no credentials.
+ * A new SP blob and will be created, while the user has no credentials.
* This can happens when we are activating an escrow token on a unsecured device, during
* which we want to create the SP structure with an empty user credential.
+ * This could also happen during an untrusted reset to clear password.
*
* 3. credentialhash == null and credential != null
* This is the untrusted credential reset, OR the user sets a new lockscreen password
@@ -2241,16 +2251,8 @@
String credential, int credentialType, int requestedQuality,
int userId) throws RemoteException {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
- // Load from the cache or a make a new one
- AuthenticationToken auth = mSpCache.get(userId);
- if (auth != null) {
- // If the synthetic password has been cached, we can only be in case 3., described
- // above, for an untrusted credential reset so a new SID is still needed.
- mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
- } else {
- auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
- credentialHash, credential, userId);
- }
+ final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
+ getGateKeeperService(), credentialHash, credential, userId);
onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
@@ -2464,36 +2466,41 @@
: "pattern"));
}
+ boolean untrustedReset = false;
if (auth != null) {
- // We are performing a trusted credential change i.e. a correct existing credential
- // is provided
- setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
- userId);
- mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
onAuthTokenKnownForUser(userId, auth);
} else if (response != null
&& response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
- // We are performing an untrusted credential change i.e. by DevicePolicyManager.
- // So provision a new SP and SID. This would invalidate existing escrow tokens.
- // Still support this for now but this flow will be removed in the next release.
+ // We are performing an untrusted credential change, by DevicePolicyManager or other
+ // internal callers that don't provide the existing credential
Slog.w(TAG, "Untrusted credential change invoked");
-
- if (mSpCache.get(userId) == null) {
- throw new IllegalStateException(
- "Untrusted credential reset not possible without cached SP");
- }
-
- initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
- userId);
- synchronizeUnifiedWorkChallengeForProfiles(userId, null);
- mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
-
- notifyActivePasswordMetricsAvailable(credential, userId);
+ // Try to get a cached auth token, so we can keep SP unchanged.
+ auth = mSpCache.get(userId);
+ untrustedReset = true;
} else /* response == null || responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ {
Slog.w(TAG, "spBasedSetLockCredentialInternalLocked: " +
(response != null ? "rate limit exceeded" : "failed"));
return;
}
+
+ if (auth != null) {
+ if (untrustedReset) {
+ // Force change the current SID to mantain existing behaviour that an untrusted
+ // reset leads to a change of SID. If the untrusted reset is for clearing the
+ // current password, the nuking of the SID will be done in
+ // setLockCredentialWithAuthTokenLocked next
+ mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
+ }
+ setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
+ userId);
+ mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
+ } else {
+ throw new IllegalStateException(
+ "Untrusted credential reset not possible without cached SP");
+ // Could call initializeSyntheticPasswordLocked(null, credential, credentialType,
+ // requestedQuality, userId) instead if we still allow untrusted reset that changes
+ // synthetic password. That would invalidate existing escrow tokens though.
+ }
mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
}
@@ -2731,7 +2738,7 @@
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mDeviceProvisionedUri.equals(uri)) {
updateRegistration();
@@ -2741,7 +2748,7 @@
clearFrpCredentialIfOwnerNotSecure();
}
} else if (mUserSetupCompleteUri.equals(uri)) {
- cleanSpCache();
+ tryRemoveUserFromSpCacheLater(userId);
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
index 59132da..285e722 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
@@ -16,10 +16,13 @@
package com.android.server.locksettings.recoverablekeystore;
+import java.io.IOException;
+import java.security.cert.CertificateException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
/**
@@ -27,6 +30,7 @@
*/
public class KeyStoreProxyImpl implements KeyStoreProxy {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private final KeyStore mKeyStore;
/**
@@ -57,4 +61,21 @@
public void deleteEntry(String alias) throws KeyStoreException {
mKeyStore.deleteEntry(alias);
}
+
+ /**
+ * Returns AndroidKeyStore-provided {@link KeyStore}, having already invoked
+ * {@link KeyStore#load(KeyStore.LoadStoreParameter)}.
+ *
+ * @throws KeyStoreException if there was a problem getting or initializing the key store.
+ */
+ public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ try {
+ keyStore.load(/*param=*/ null);
+ } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
+ // Should never happen.
+ throw new KeyStoreException("Unable to load keystore.", e);
+ }
+ return keyStore;
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 662ffc8..dee24c7 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -44,6 +44,7 @@
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -176,8 +177,17 @@
return;
}
- PublicKey publicKey = mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId,
+ PublicKey publicKey;
+ CertPath certPath = mRecoverableKeyStoreDb.getRecoveryServiceCertPath(mUserId,
recoveryAgentUid);
+ if (certPath != null) {
+ Log.d(TAG, "Using the public key in stored CertPath for syncing");
+ publicKey = certPath.getCertificates().get(0).getPublicKey();
+ } else {
+ Log.d(TAG, "Using the stored raw public key for syncing");
+ publicKey = mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId,
+ recoveryAgentUid);
+ }
if (publicKey == null) {
Log.w(TAG, "Not initialized for KeySync: no public key set. Cancelling task.");
return;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 3a78f95..e5ff5b8 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -194,6 +194,9 @@
UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
init(userId);
try {
+ // Try to see if the decryption key is still accessible before using the encryption key.
+ // The auth-bound decryption will be unrecoverable if the screen lock is disabled.
+ getDecryptKeyInternal(userId);
return getEncryptKeyInternal(userId);
} catch (UnrecoverableKeyException e) {
Log.i(TAG, String.format(Locale.US,
@@ -219,7 +222,7 @@
UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
int generationId = getGenerationId(userId);
String alias = getEncryptAlias(userId, generationId);
- if (!mKeyStore.containsAlias(alias)) {
+ if (!isKeyLoaded(userId, generationId)) {
throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
}
AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
@@ -268,7 +271,7 @@
UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
int generationId = getGenerationId(userId);
String alias = getDecryptAlias(userId, generationId);
- if (!mKeyStore.containsAlias(alias)) {
+ if (!isKeyLoaded(userId, generationId)) {
throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
}
AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
@@ -300,12 +303,12 @@
return;
}
if (generationId == -1) {
- Log.i(TAG, "Generating initial platform ID.");
+ Log.i(TAG, "Generating initial platform key generation ID.");
generationId = 1;
} else {
Log.w(TAG, String.format(Locale.US, "Platform generation ID was %d but no "
+ "entry was present in AndroidKeyStore. Generating fresh key.", generationId));
- // Had to generate a fresh key, bump the generation id
+ // Have to generate a fresh key, so bump the generation id
generationId++;
}
@@ -374,7 +377,7 @@
String decryptAlias = getDecryptAlias(userId, generationId);
SecretKey secretKey = generateAesKey();
- // Store Since decryption key first since it is more likely to fail.
+ // Store decryption key first since it is more likely to fail.
mKeyStore.setEntry(
decryptAlias,
new KeyStore.SecretKeyEntry(secretKey),
@@ -386,7 +389,6 @@
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setBoundToSpecificSecureUserId(userId)
.build());
-
mKeyStore.setEntry(
encryptAlias,
new KeyStore.SecretKeyEntry(secretKey),
@@ -397,11 +399,7 @@
setGenerationId(userId, generationId);
- try {
- secretKey.destroy();
- } catch (DestroyFailedException e) {
- Log.w(TAG, "Failed to destroy in-memory platform key.", e);
- }
+ // TODO: Use a reliable way to destroy the temporary secretKey in memory.
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index ec72b22..9f82268 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -23,34 +23,41 @@
import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
import static android.security.keystore.RecoveryController.ERROR_SESSION_EXPIRED;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
-import android.Manifest;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.KeyStore;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertXml;
+import com.android.server.locksettings.recoverablekeystore.certificate.TrustedRootCert;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
import java.security.InvalidKeyException;
-import java.security.KeyStoreException;
import java.security.KeyFactory;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
@@ -82,18 +89,23 @@
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
private final PlatformKeyManager mPlatformKeyManager;
+ private final KeyStore mKeyStore;
+ private final ApplicationKeyStorage mApplicationKeyStorage;
/**
* Returns a new or existing instance.
*
* @hide
*/
- public static synchronized RecoverableKeyStoreManager getInstance(Context context) {
+ public static synchronized RecoverableKeyStoreManager
+ getInstance(Context context, KeyStore keystore) {
if (mInstance == null) {
RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
PlatformKeyManager platformKeyManager;
+ ApplicationKeyStorage applicationKeyStorage;
try {
platformKeyManager = PlatformKeyManager.getInstance(context, db);
+ applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore);
} catch (NoSuchAlgorithmException e) {
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);
@@ -103,12 +115,14 @@
mInstance = new RecoverableKeyStoreManager(
context.getApplicationContext(),
+ keystore,
db,
new RecoverySessionStorage(),
Executors.newSingleThreadExecutor(),
new RecoverySnapshotStorage(),
new RecoverySnapshotListenersStorage(),
- platformKeyManager);
+ platformKeyManager,
+ applicationKeyStorage);
}
return mInstance;
}
@@ -116,19 +130,23 @@
@VisibleForTesting
RecoverableKeyStoreManager(
Context context,
+ KeyStore keystore,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
ExecutorService executorService,
RecoverySnapshotStorage snapshotStorage,
RecoverySnapshotListenersStorage listenersStorage,
- PlatformKeyManager platformKeyManager) {
+ PlatformKeyManager platformKeyManager,
+ ApplicationKeyStorage applicationKeyStorage) {
mContext = context;
+ mKeyStore = keystore;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
mListenersStorage = listenersStorage;
mSnapshotStorage = snapshotStorage;
mPlatformKeyManager = platformKeyManager;
+ mApplicationKeyStorage = applicationKeyStorage;
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
@@ -139,18 +157,67 @@
}
public void initRecoveryService(
- @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
+ @NonNull String rootCertificateAlias, @NonNull byte[] recoveryServiceCertFile)
throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
- // TODO: open /system/etc/security/... cert file, and check the signature on the public keys
- PublicKey publicKey;
+
+ // TODO: Check the public-key signature on the whole file before parsing it
+
+ CertXml certXml;
+ try {
+ certXml = CertXml.parse(recoveryServiceCertFile);
+ } catch (CertParsingException e) {
+ // TODO: Do not use raw key bytes anymore once the other components are updated
+ Log.d(TAG, "Failed to parse the cert file", e);
+ PublicKey publicKey = parseEcPublicKey(recoveryServiceCertFile);
+ if (mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey) > 0) {
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
+ return;
+ }
+
+ // Check serial number
+ long newSerial = certXml.getSerial();
+ Long oldSerial = mDatabase.getRecoveryServiceCertSerial(userId, uid);
+ if (oldSerial != null && oldSerial >= newSerial) {
+ if (oldSerial == newSerial) {
+ Log.i(TAG, "The cert file serial number is the same, so skip updating.");
+ } else {
+ Log.e(TAG, "The cert file serial number is older than the one in database.");
+ }
+ return;
+ }
+ Log.i(TAG, "Updating the certificate with the new serial number " + newSerial);
+
+ CertPath certPath;
+ try {
+ Log.d(TAG, "Getting and validating a random endpoint certificate");
+ certPath = certXml.getRandomEndpointCert(TrustedRootCert.TRUSTED_ROOT_CERT);
+ } catch (CertValidationException e) {
+ Log.e(TAG, "Invalid endpoint cert", e);
+ throw new ServiceSpecificException(
+ ERROR_BAD_CERTIFICATE_FORMAT, "Failed to validate certificate.");
+ }
+ try {
+ Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
+ if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
+ mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
+ } catch (CertificateEncodingException e) {
+ Log.e(TAG, "Failed to encode CertPath", e);
+ throw new ServiceSpecificException(
+ ERROR_BAD_CERTIFICATE_FORMAT, "Failed to encode CertPath.");
+ }
+ }
+
+ private PublicKey parseEcPublicKey(@NonNull byte[] bytes) throws ServiceSpecificException {
try {
KeyFactory kf = KeyFactory.getInstance("EC");
- // TODO: Randomly choose a key from the list -- right now we just use the whole input
- X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList);
- publicKey = kf.generatePublic(pkSpec);
+ X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(bytes);
+ return kf.generatePublic(pkSpec);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e);
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
@@ -158,10 +225,6 @@
throw new ServiceSpecificException(
ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 certificate.");
}
- long updatedRows = mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey);
- if (updatedRows > 0) {
- mDatabase.setShouldCreateSnapshot(userId, uid, true);
- }
}
/**
@@ -406,6 +469,7 @@
}
/**
+ * Deprecated
* Generates a key named {@code alias} in the recoverable store for the calling uid. Then
* returns the raw key material.
*
@@ -450,20 +514,66 @@
boolean wasRemoved = mDatabase.removeKey(uid, alias);
if (wasRemoved) {
mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ mApplicationKeyStorage.deleteEntry(userId, uid, alias);
}
}
+ /**
+ * Generates a key named {@code alias} in caller's namespace.
+ * The key is stored in system service keystore namespace.
+ *
+ * @return grant alias, which caller can use to access the key.
+ */
+ public String generateKey(@NonNull String alias, byte[] account) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+
+ PlatformEncryptionKey encryptionKey;
+ try {
+ encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
+ } catch (NoSuchAlgorithmException e) {
+ // Impossible: all algorithms must be supported by AOSP
+ throw new RuntimeException(e);
+ } catch (KeyStoreException | UnrecoverableKeyException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ } catch (InsecureUserException e) {
+ throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
+ }
+
+ try {
+ byte[] secretKey =
+ mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
+ mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
+ String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ return grantAlias;
+ } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Gets a key named {@code alias} in caller's namespace.
+ *
+ * @return grant alias, which caller can use to access the key.
+ */
+ public String getKey(@NonNull String alias) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ return grantAlias;
+ }
+
private byte[] decryptRecoveryKey(
RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse)
throws RemoteException, ServiceSpecificException {
byte[] locallyEncryptedKey;
try {
// TODO: Remove the extraneous logging here
- Log.e(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
+ Log.d(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
sessionEntry.getKeyClaimant()));
- Log.e(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
+ Log.d(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
sessionEntry.getVaultParams()));
- Log.e(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
+ Log.d(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
locallyEncryptedKey = KeySyncUtils.decryptRecoveryClaimResponse(
sessionEntry.getKeyClaimant(),
sessionEntry.getVaultParams(),
@@ -483,9 +593,9 @@
try {
// TODO: Remove the extraneous logging here
- Log.e(TAG, constructLoggingMessage("sessionEntry.getLskfHash()",
+ Log.d(TAG, constructLoggingMessage("sessionEntry.getLskfHash()",
sessionEntry.getLskfHash()));
- Log.e(TAG, constructLoggingMessage("locallyEncryptedKey", locallyEncryptedKey));
+ Log.d(TAG, constructLoggingMessage("locallyEncryptedKey", locallyEncryptedKey));
return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
} catch (InvalidKeyException e) {
Log.e(TAG, "Got InvalidKeyException during decrypting recovery key", e);
@@ -525,8 +635,8 @@
try {
// TODO: Remove the extraneous logging here
- Log.e(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
- Log.e(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
+ Log.d(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
+ Log.d(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
byte[] keyMaterial =
KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
keyMaterialByAlias.put(alias, keyMaterial);
@@ -540,13 +650,16 @@
throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
"Failed to recover key with alias '" + alias + "': " + e.getMessage());
} catch (AEADBadTagException e) {
- // TODO: Remove the extraneous logging here
Log.e(TAG, "Got AEADBadTagException during decrypting application key with alias: "
+ alias, e);
- throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
- "Failed to recover key with alias '" + alias + "': " + e.getMessage());
+ // Ignore the exception to continue to recover the other application keys.
}
}
+ if (keyMaterialByAlias.isEmpty()) {
+ Log.e(TAG, "Failed to recover any of the application keys.");
+ throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
+ "Failed to recover any of the application keys.");
+ }
return keyMaterialByAlias;
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertParsingException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertParsingException.java
new file mode 100644
index 0000000..57a3d99
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertParsingException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+/** Exception thrown when parsing errors occur. */
+public class CertParsingException extends Exception {
+
+ public CertParsingException(String message) {
+ super(message);
+ }
+
+ public CertParsingException(Exception cause) {
+ super(cause);
+ }
+
+ public CertParsingException(String message, Exception cause) {
+ super(message, cause);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
new file mode 100644
index 0000000..09ec5ad
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -0,0 +1,355 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import static javax.xml.xpath.XPathConstants.NODESET;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/** Utility functions related to parsing and validating public-key certificates. */
+public final class CertUtils {
+
+ private static final String CERT_FORMAT = "X.509";
+ private static final String CERT_PATH_ALG = "PKIX";
+ private static final String CERT_STORE_ALG = "Collection";
+ private static final String SIGNATURE_ALG = "SHA256withRSA";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MUST_EXIST_UNENFORCED, MUST_EXIST_EXACTLY_ONE, MUST_EXIST_AT_LEAST_ONE})
+ @interface MustExist {}
+ static final int MUST_EXIST_UNENFORCED = 0;
+ static final int MUST_EXIST_EXACTLY_ONE = 1;
+ static final int MUST_EXIST_AT_LEAST_ONE = 2;
+
+ private CertUtils() {}
+
+ /**
+ * Decodes a byte array containing an encoded X509 certificate.
+ *
+ * @param certBytes the byte array containing the encoded X509 certificate
+ * @return the decoded X509 certificate
+ * @throws CertParsingException if any parsing error occurs
+ */
+ static X509Certificate decodeCert(byte[] certBytes) throws CertParsingException {
+ return decodeCert(new ByteArrayInputStream(certBytes));
+ }
+
+ /**
+ * Decodes an X509 certificate from an {@code InputStream}.
+ *
+ * @param inStream the input stream containing the encoded X509 certificate
+ * @return the decoded X509 certificate
+ * @throws CertParsingException if any parsing error occurs
+ */
+ static X509Certificate decodeCert(InputStream inStream) throws CertParsingException {
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance(CERT_FORMAT);
+ } catch (CertificateException e) {
+ // Should not happen, as X.509 is mandatory for all providers.
+ throw new RuntimeException(e);
+ }
+ try {
+ return (X509Certificate) certFactory.generateCertificate(inStream);
+ } catch (CertificateException e) {
+ throw new CertParsingException(e);
+ }
+ }
+
+ /**
+ * Parses a byte array as the content of an XML file, and returns the root node of the XML file.
+ *
+ * @param xmlBytes the byte array that is the XML file content
+ * @return the root node of the XML file
+ * @throws CertParsingException if any parsing error occurs
+ */
+ static Element getXmlRootNode(byte[] xmlBytes) throws CertParsingException {
+ try {
+ Document document =
+ DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder()
+ .parse(new ByteArrayInputStream(xmlBytes));
+ document.getDocumentElement().normalize();
+ return document.getDocumentElement();
+ } catch (SAXException | ParserConfigurationException | IOException e) {
+ throw new CertParsingException(e);
+ }
+ }
+
+ /**
+ * Gets the text contents of certain XML child nodes, given a XML root node and a list of tags
+ * representing the path to locate the child nodes. The whitespaces and newlines in the text
+ * contents are stripped away.
+ *
+ * <p>For example, the list of tags [tag1, tag2, tag3] represents the XML tree like the
+ * following:
+ *
+ * <pre>
+ * <root>
+ * <tag1>
+ * <tag2>
+ * <tag3>abc</tag3>
+ * <tag3>def</tag3>
+ * </tag2>
+ * </tag1>
+ * <root>
+ * </pre>
+ *
+ * @param mustExist whether and how many nodes must exist. If the number of child nodes does not
+ * satisfy the requirement, CertParsingException will be thrown.
+ * @param rootNode the root node that serves as the starting point to locate the child nodes
+ * @param nodeTags the list of tags representing the relative path from the root node
+ * @return a list of strings that are the text contents of the child nodes
+ * @throws CertParsingException if any parsing error occurs
+ */
+ static List<String> getXmlNodeContents(@MustExist int mustExist, Element rootNode,
+ String... nodeTags)
+ throws CertParsingException {
+ String expression = String.join("/", nodeTags);
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList nodeList;
+ try {
+ nodeList = (NodeList) xPath.compile(expression).evaluate(rootNode, NODESET);
+ } catch (XPathExpressionException e) {
+ throw new CertParsingException(e);
+ }
+
+ switch (mustExist) {
+ case MUST_EXIST_UNENFORCED:
+ break;
+
+ case MUST_EXIST_EXACTLY_ONE:
+ if (nodeList.getLength() != 1) {
+ throw new CertParsingException(
+ "The XML file must contain exactly one node with the path "
+ + expression);
+ }
+ break;
+
+ case MUST_EXIST_AT_LEAST_ONE:
+ if (nodeList.getLength() == 0) {
+ throw new CertParsingException(
+ "The XML file must contain at least one node with the path "
+ + expression);
+ }
+ break;
+
+ default:
+ throw new UnsupportedOperationException(
+ "This value of MustExist is not supported: " + mustExist);
+ }
+
+ List<String> result = new ArrayList<>();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ // Remove whitespaces and newlines.
+ result.add(node.getTextContent().replaceAll("\\s", ""));
+ }
+ return result;
+ }
+
+ /**
+ * Decodes a base64-encoded string.
+ *
+ * @param str the base64-encoded string
+ * @return the decoding decoding result
+ * @throws CertParsingException if the input string is not a properly base64-encoded string
+ */
+ public static byte[] decodeBase64(String str) throws CertParsingException {
+ try {
+ return Base64.getDecoder().decode(str);
+ } catch (IllegalArgumentException e) {
+ throw new CertParsingException(e);
+ }
+ }
+
+ /**
+ * Verifies a public-key signature that is computed by RSA with SHA256.
+ *
+ * @param signerPublicKey the public key of the original signer
+ * @param signature the public-key signature
+ * @param signedBytes the bytes that have been signed
+ * @throws CertValidationException if the signature verification fails
+ */
+ static void verifyRsaSha256Signature(
+ PublicKey signerPublicKey, byte[] signature, byte[] signedBytes)
+ throws CertValidationException {
+ Signature verifier;
+ try {
+ verifier = Signature.getInstance(SIGNATURE_ALG);
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen, as SHA256withRSA is mandatory for all providers.
+ throw new RuntimeException(e);
+ }
+ try {
+ verifier.initVerify(signerPublicKey);
+ verifier.update(signedBytes);
+ if (!verifier.verify(signature)) {
+ throw new CertValidationException("The signature is invalid");
+ }
+ } catch (InvalidKeyException | SignatureException e) {
+ throw new CertValidationException(e);
+ }
+ }
+
+ /**
+ * Validates a leaf certificate, and returns the certificate path if the certificate is valid.
+ * If the given validation date is null, the current date will be used.
+ *
+ * @param validationDate the date for which the validity of the certificate should be
+ * determined
+ * @param trustedRoot the certificate of the trusted root CA
+ * @param intermediateCerts the list of certificates of possible intermediate CAs
+ * @param leafCert the leaf certificate that is to be validated
+ * @return the certificate path if the leaf cert is valid
+ * @throws CertValidationException if {@code leafCert} is invalid (e.g., is expired, or has
+ * invalid signature)
+ */
+ static CertPath validateCert(
+ @Nullable Date validationDate,
+ X509Certificate trustedRoot,
+ List<X509Certificate> intermediateCerts,
+ X509Certificate leafCert)
+ throws CertValidationException {
+ PKIXParameters pkixParams =
+ buildPkixParams(validationDate, trustedRoot, intermediateCerts, leafCert);
+ CertPath certPath = buildCertPath(pkixParams);
+
+ CertPathValidator certPathValidator;
+ try {
+ certPathValidator = CertPathValidator.getInstance(CERT_PATH_ALG);
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen, as PKIX is mandatory for all providers.
+ throw new RuntimeException(e);
+ }
+ try {
+ certPathValidator.validate(certPath, pkixParams);
+ } catch (CertPathValidatorException | InvalidAlgorithmParameterException e) {
+ throw new CertValidationException(e);
+ }
+ return certPath;
+ }
+
+ @VisibleForTesting
+ static CertPath buildCertPath(PKIXParameters pkixParams) throws CertValidationException {
+ CertPathBuilder certPathBuilder;
+ try {
+ certPathBuilder = CertPathBuilder.getInstance(CERT_PATH_ALG);
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen, as PKIX is mandatory for all providers.
+ throw new RuntimeException(e);
+ }
+ try {
+ return certPathBuilder.build(pkixParams).getCertPath();
+ } catch (CertPathBuilderException | InvalidAlgorithmParameterException e) {
+ throw new CertValidationException(e);
+ }
+ }
+
+ @VisibleForTesting
+ static PKIXParameters buildPkixParams(
+ @Nullable Date validationDate,
+ X509Certificate trustedRoot,
+ List<X509Certificate> intermediateCerts,
+ X509Certificate leafCert)
+ throws CertValidationException {
+ // Create a TrustAnchor from the trusted root certificate.
+ Set<TrustAnchor> trustedAnchors = new HashSet<>();
+ trustedAnchors.add(new TrustAnchor(trustedRoot, null));
+
+ // Create a CertStore from the list of intermediate certificates.
+ List<X509Certificate> certs = new ArrayList<>(intermediateCerts);
+ certs.add(leafCert);
+ CertStore certStore;
+ try {
+ certStore =
+ CertStore.getInstance(CERT_STORE_ALG, new CollectionCertStoreParameters(certs));
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen, as Collection is mandatory for all providers.
+ throw new RuntimeException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new CertValidationException(e);
+ }
+
+ // Create a CertSelector from the leaf certificate.
+ X509CertSelector certSelector = new X509CertSelector();
+ certSelector.setCertificate(leafCert);
+
+ // Build a PKIXParameters from TrustAnchor, CertStore, and CertSelector.
+ PKIXBuilderParameters pkixParams;
+ try {
+ pkixParams = new PKIXBuilderParameters(trustedAnchors, certSelector);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new CertValidationException(e);
+ }
+ pkixParams.addCertStore(certStore);
+
+ // If validationDate is null, the current time will be used.
+ pkixParams.setDate(validationDate);
+ pkixParams.setRevocationEnabled(false);
+
+ return pkixParams;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertValidationException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertValidationException.java
new file mode 100644
index 0000000..99a9ff7
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertValidationException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+/** Exception thrown when validation or verification fails. */
+public class CertValidationException extends Exception {
+
+ public CertValidationException(String message) {
+ super(message);
+ }
+
+ public CertValidationException(Exception cause) {
+ super(cause);
+ }
+
+ public CertValidationException(String message, Exception cause) {
+ super(message, cause);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
new file mode 100644
index 0000000..c62a31e
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
@@ -0,0 +1,178 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.SecureRandom;
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+/**
+ * Parses and holds the XML file containing the list of THM public-key certificates and related
+ * metadata.
+ */
+public final class CertXml {
+
+ private static final String METADATA_NODE_TAG = "metadata";
+ private static final String METADATA_SERIAL_NODE_TAG = "serial";
+ private static final String METADATA_REFRESH_INTERVAL_NODE_TAG = "refresh-interval";
+ private static final String ENDPOINT_CERT_LIST_TAG = "endpoints";
+ private static final String ENDPOINT_CERT_ITEM_TAG = "cert";
+ private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates";
+ private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert";
+
+ private final long serial;
+ private final long refreshInterval;
+ private final List<X509Certificate> intermediateCerts;
+ private final List<X509Certificate> endpointCerts;
+
+ private CertXml(
+ long serial,
+ long refreshInterval,
+ List<X509Certificate> intermediateCerts,
+ List<X509Certificate> endpointCerts) {
+ this.serial = serial;
+ this.refreshInterval = refreshInterval;
+ this.intermediateCerts = intermediateCerts;
+ this.endpointCerts = endpointCerts;
+ }
+
+ /** Gets the serial number of the XML file containing public-key certificates. */
+ public long getSerial() {
+ return serial;
+ }
+
+ /**
+ * Gets the refresh interval in the XML file containing public-key certificates. The refresh
+ * interval denotes the number of seconds that the client should follow to contact the server to
+ * refresh the XML file.
+ */
+ public long getRefreshInterval() {
+ return refreshInterval;
+ }
+
+ @VisibleForTesting
+ List<X509Certificate> getAllIntermediateCerts() {
+ return intermediateCerts;
+ }
+
+ @VisibleForTesting
+ List<X509Certificate> getAllEndpointCerts() {
+ return endpointCerts;
+ }
+
+ /**
+ * Chooses a random endpoint certificate from the XML file, validates the chosen certificate,
+ * and returns the certificate path including the chosen certificate if it is valid.
+ *
+ * @param trustedRoot the trusted root certificate
+ * @return the certificate path including the chosen certificate if the certificate is valid
+ * @throws CertValidationException if the chosen certificate cannot be validated based on the
+ * trusted root certificate
+ */
+ public CertPath getRandomEndpointCert(X509Certificate trustedRoot)
+ throws CertValidationException {
+ return getEndpointCert(
+ new SecureRandom().nextInt(this.endpointCerts.size()),
+ /*validationDate=*/ null,
+ trustedRoot);
+ }
+
+ @VisibleForTesting
+ CertPath getEndpointCert(
+ int index, @Nullable Date validationDate, X509Certificate trustedRoot)
+ throws CertValidationException {
+ X509Certificate chosenCert = endpointCerts.get(index);
+ return CertUtils.validateCert(validationDate, trustedRoot, intermediateCerts, chosenCert);
+ }
+
+ /**
+ * Parses a byte array as the content of the XML file containing a list of endpoint
+ * certificates.
+ *
+ * @param bytes the bytes of the XML file
+ * @return a {@code CertXml} instance that contains the parsing result
+ * @throws CertParsingException if any parsing error occurs
+ */
+ public static CertXml parse(byte[] bytes) throws CertParsingException {
+ Element rootNode = CertUtils.getXmlRootNode(bytes);
+ return new CertXml(
+ parseSerial(rootNode),
+ parseRefreshInterval(rootNode),
+ parseIntermediateCerts(rootNode),
+ parseEndpointCerts(rootNode));
+ }
+
+ private static long parseSerial(Element rootNode) throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_EXACTLY_ONE,
+ rootNode,
+ METADATA_NODE_TAG,
+ METADATA_SERIAL_NODE_TAG);
+ return Long.parseLong(contents.get(0));
+ }
+
+ private static long parseRefreshInterval(Element rootNode) throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_EXACTLY_ONE,
+ rootNode,
+ METADATA_NODE_TAG,
+ METADATA_REFRESH_INTERVAL_NODE_TAG);
+ return Long.parseLong(contents.get(0));
+ }
+
+ private static List<X509Certificate> parseIntermediateCerts(Element rootNode)
+ throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_UNENFORCED,
+ rootNode,
+ INTERMEDIATE_CERT_LIST_TAG,
+ INTERMEDIATE_CERT_ITEM_TAG);
+ List<X509Certificate> res = new ArrayList<>();
+ for (String content : contents) {
+ res.add(CertUtils.decodeCert(CertUtils.decodeBase64(content)));
+ }
+ return Collections.unmodifiableList(res);
+ }
+
+ private static List<X509Certificate> parseEndpointCerts(Element rootNode)
+ throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_AT_LEAST_ONE,
+ rootNode,
+ ENDPOINT_CERT_LIST_TAG,
+ ENDPOINT_CERT_ITEM_TAG);
+ List<X509Certificate> res = new ArrayList<>();
+ for (String content : contents) {
+ res.add(CertUtils.decodeCert(CertUtils.decodeBase64(content)));
+ }
+ return Collections.unmodifiableList(res);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java
new file mode 100644
index 0000000..e75be85
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java
@@ -0,0 +1,121 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+/**
+ * Parses and holds the XML file containing the signature of the XML file containing the list of THM
+ * public-key certificates.
+ */
+public final class SigXml {
+
+ private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates";
+ private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert";
+ private static final String SIGNER_CERT_NODE_TAG = "certificate";
+ private static final String SIGNATURE_NODE_TAG = "value";
+
+ private final List<X509Certificate> intermediateCerts;
+ private final X509Certificate signerCert;
+ private final byte[] signature;
+
+ private SigXml(
+ List<X509Certificate> intermediateCerts, X509Certificate signerCert, byte[] signature) {
+ this.intermediateCerts = intermediateCerts;
+ this.signerCert = signerCert;
+ this.signature = signature;
+ }
+
+ /**
+ * Verifies the signature contained in this XML file against a trusted root certificate and the
+ * binary content of another file. The signer's public-key certificate and possible intermediate
+ * CA certificates are included in this XML file, and will be validated against the trusted root
+ * certificate.
+ *
+ * @param trustedRoot the trusted root certificate
+ * @param signedFileBytes the original file content that has been signed
+ * @throws CertValidationException if the signature verification fails, or the signer's
+ * certificate contained in this XML file cannot be validated
+ * based on the trusted root certificate
+ */
+ public void verifyFileSignature(X509Certificate trustedRoot, byte[] signedFileBytes)
+ throws CertValidationException {
+ verifyFileSignature(trustedRoot, signedFileBytes, /*validationDate=*/ null);
+ }
+
+ @VisibleForTesting
+ void verifyFileSignature(
+ X509Certificate trustedRoot, byte[] signedFileBytes, @Nullable Date validationDate)
+ throws CertValidationException {
+ CertUtils.validateCert(validationDate, trustedRoot, intermediateCerts, signerCert);
+ CertUtils.verifyRsaSha256Signature(signerCert.getPublicKey(), signature, signedFileBytes);
+ }
+
+ /**
+ * Parses a byte array as the content of the XML file containing the signature and related
+ * certificates.
+ *
+ * @param bytes the bytes of the XML file
+ * @return a {@code SigXml} instance that contains the parsing result
+ * @throws CertParsingException if any parsing error occurs
+ */
+ public static SigXml parse(byte[] bytes) throws CertParsingException {
+ Element rootNode = CertUtils.getXmlRootNode(bytes);
+ return new SigXml(
+ parseIntermediateCerts(rootNode), parseSignerCert(rootNode),
+ parseFileSignature(rootNode));
+ }
+
+ private static List<X509Certificate> parseIntermediateCerts(Element rootNode)
+ throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_UNENFORCED,
+ rootNode,
+ INTERMEDIATE_CERT_LIST_TAG,
+ INTERMEDIATE_CERT_ITEM_TAG);
+ List<X509Certificate> res = new ArrayList<>();
+ for (String content : contents) {
+ res.add(CertUtils.decodeCert(CertUtils.decodeBase64(content)));
+ }
+ return Collections.unmodifiableList(res);
+ }
+
+ private static X509Certificate parseSignerCert(Element rootNode) throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNER_CERT_NODE_TAG);
+ return CertUtils.decodeCert(CertUtils.decodeBase64(contents.get(0)));
+ }
+
+ private static byte[] parseFileSignature(Element rootNode) throws CertParsingException {
+ List<String> contents =
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNATURE_NODE_TAG);
+ return CertUtils.decodeBase64(contents.get(0));
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
new file mode 100644
index 0000000..7195d62
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
@@ -0,0 +1,74 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * Holds the X509 certificate of the trusted root CA cert for the recoverable key store service.
+ *
+ * TODO: Read the certificate from a PEM file directly and remove this class.
+ */
+public final class TrustedRootCert {
+
+ private static final String TRUSTED_ROOT_CERT_BASE64 = ""
+ + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
+ + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx"
+ + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
+ + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2OT5i40/H7LINg/lq/0G0hR65P"
+ + "Q4Mud3OnuVt6UIYV2T18+v6qW1yJd5FcnND/ZKPau4aUAYklqJuSVjOXQD0BjgS2"
+ + "98Xa4dSn8Ci1rUR+5tdmrxqbYUdT2ZvJIUMMR6fRoqi+LlAbKECrV+zYQTyLU68w"
+ + "V66hQpAButjJKiZzkXjmKLfJ5IWrNEn17XM988rk6qAQn/BYCCQGf3rQuJeksGmA"
+ + "N1lJOwNYxmWUyouVwqwZthNEWqTuEyBFMkAT+99PXW7oVDc7oU5cevuihxQWNTYq"
+ + "viGB8cck6RW3cmqrDSaJF/E+N0cXFKyYC7FDcggt6k3UrxNKTuySdDEa8+2RTQqU"
+ + "Y9npxBlQE+x9Ig56OI1BG3bSBsGdPgjpyHadZeh2tgk+oqlGsSsum24YxaxuSysT"
+ + "Qfcu/XhyfUXavfmGrBOXerTzIl5oBh/F5aHTV85M2tYEG0qsPPvSpZAWtdJ/2rca"
+ + "OxvhwOL+leZKr8McjXVR00lBsRuKXX4nTUMwya09CO3QHFPFZtZvqjy2HaMOnVLQ"
+ + "I6b6dHEfmsHybzVOe3yPEoFQSU9UhUdmi71kwwoanPD3j9fJHmXTx4PzYYBRf1ZE"
+ + "o+uPgMPk7CDKQFZLjnR40z1uzu3O8aZ3AKZzP+j7T4XQKJLQLmllKtPgLgNdJyib"
+ + "2Glg7QhXH/jBTL6hAgMBAAGjYzBhMB0GA1UdDgQWBBSbZfrqOYH54EJpkdKMZjMc"
+ + "z/Hp+DAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DAPBgNVHRMBAf8E"
+ + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQ0FAAOCAgEAKh9nm/vW"
+ + "glMWp3vcCwWwJW286ecREDlI+CjGh5h+f2N4QRrXd/tKE3qQJWCqGx8sFfIUjmI7"
+ + "KYdsC2gyQ2cA2zl0w7pB2QkuqE6zVbnh1D17Hwl19IMyAakFaM9ad4/EoH7oQmqX"
+ + "nF/f5QXGZw4kf1HcgKgoCHWXjqR8MqHOcXR8n6WFqxjzJf1jxzi6Yo2dZ7PJbnE6"
+ + "+kHIJuiCpiHL75v5g1HM41gT3ddFFSrn88ThNPWItT5Z8WpFjryVzank2Yt02LLl"
+ + "WqZg9IC375QULc5B58NMnaiVJIDJQ8zoNgj1yaxqtUMnJX570lotO2OXe4ec9aCQ"
+ + "DIJ84YLM/qStFdeZ9416E80dchskbDG04GuVJKlzWjxAQNMRFhyaPUSBTLLg+kwP"
+ + "t9+AMmc+A7xjtFQLZ9fBYHOBsndJOmeSQeYeckl+z/1WQf7DdwXn/yijon7mxz4z"
+ + "cCczfKwTJTwBh3wR5SQr2vQm7qaXM87qxF8PCAZrdZaw5I80QwkgTj0WTZ2/GdSw"
+ + "d3o5SyzzBAjpwtG+4bO/BD9h9wlTsHpT6yWOZs4OYAKU5ykQrncI8OyavMggArh3"
+ + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
+ + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
+
+ /**
+ * The X509 certificate of the trusted root CA cert for the recoverable key store service.
+ *
+ * TODO: Change it to the production certificate root CA before the final launch.
+ */
+ public static final X509Certificate TRUSTED_ROOT_CERT;
+
+ static {
+ try {
+ TRUSTED_ROOT_CERT = CertUtils.decodeCert(
+ CertUtils.decodeBase64(TRUSTED_ROOT_CERT_BASE64));
+ } catch (CertParsingException e) {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
new file mode 100644
index 0000000..600a534
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -0,0 +1,113 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.storage;
+
+import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
+
+import android.annotation.Nullable;
+import android.os.ServiceSpecificException;
+import android.security.Credentials;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.KeyStore;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
+import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl;
+
+import java.security.KeyStore.SecretKeyEntry;
+import java.security.KeyStoreException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Storage for Application keys in LockSettings service KeyStore namespace.
+ *
+ * <p> Uses KeyStore's grant mechanism to make keys usable by application process without
+ * revealing key material
+ */
+public class ApplicationKeyStorage {
+ private static final String APPLICATION_KEY_ALIAS_PREFIX =
+ "com.android.server.locksettings.recoverablekeystore/application/";
+
+ KeyStoreProxy mKeyStore;
+ KeyStore mKeystoreService;
+
+ public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
+ throws KeyStoreException {
+ return new ApplicationKeyStorage(
+ new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()),
+ keystoreService);
+ }
+
+ @VisibleForTesting
+ ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) {
+ mKeyStore = keyStore;
+ mKeystoreService = keystoreService;
+ }
+
+ /**
+ * Returns grant alias, valid in Applications namespace.
+ */
+ public @Nullable String getGrantAlias(int userId, int uid, String alias) {
+ // Aliases used by {@link KeyStore} are different than used by public API.
+ // {@code USER_PRIVATE_KEY} prefix is used secret keys.
+ String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
+ return mKeystoreService.grant(keystoreAlias, uid);
+ }
+
+ public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
+ throws KeyStoreException {
+ try {
+ mKeyStore.setEntry(
+ getInternalAlias(userId, uid, alias),
+ new SecretKeyEntry(
+ new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ public void deleteEntry(int userId, int uid, String alias) {
+ try {
+ mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Returns the alias in locksettins service's KeyStore namespace used for given application key.
+ *
+ * <p>These IDs look as follows:
+ * {@code com.security.recoverablekeystore/application/<userId>/<uid>/<alias>}
+ *
+ * @param userId The ID of the user
+ * @param uid The uid
+ * @param alias - alias in application's namespace
+ * @return The alias.
+ */
+ private String getInternalAlias(int userId, int uid, String alias) {
+ return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index b96208d..89ddb6c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -31,9 +31,14 @@
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
+import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
@@ -53,6 +58,7 @@
private static final String TAG = "RecoverableKeyStoreDb";
private static final int IDLE_TIMEOUT_SECONDS = 30;
private static final int LAST_SYNCED_AT_UNSYNCED = -1;
+ private static final String CERT_PATH_ENCODING = "PkiPath";
private final RecoverableKeyStoreDbHelper mKeyStoreDbHelper;
@@ -361,6 +367,79 @@
}
/**
+ * Returns the serial number of the XML file containing certificates of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @return The value that were previously set, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ public Long getRecoveryServiceCertSerial(int userId, int uid) {
+ return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
+ }
+
+ /**
+ * Records the serial number of the XML file containing certificates of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @param serial The serial number contained in the XML file for recovery service certificates.
+ * @return The primary key of the inserted row, or -1 if failed.
+ *
+ * @hide
+ */
+ public long setRecoveryServiceCertSerial(int userId, int uid, long serial) {
+ return setLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, serial);
+ }
+
+ /**
+ * Returns the {@code CertPath} of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @return The value that were previously set, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ public CertPath getRecoveryServiceCertPath(int userId, int uid) {
+ byte[] bytes = getBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
+ if (bytes == null) {
+ return null;
+ }
+ try {
+ return decodeCertPath(bytes);
+ } catch (CertificateException e) {
+ Log.wtf(TAG,
+ String.format(Locale.US,
+ "Recovery service CertPath entry cannot be decoded for "
+ + "userId=%d uid=%d.",
+ userId, uid), e);
+ return null;
+ }
+ }
+
+ /**
+ * Sets the {@code CertPath} of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @param certPath The certificate path of the recovery service.
+ * @return The primary key of the inserted row, or -1 if failed.
+ * @hide
+ */
+ public long setRecoveryServiceCertPath(int userId, int uid, CertPath certPath) throws
+ CertificateEncodingException {
+ if (certPath.getCertificates().size() == 0) {
+ throw new CertificateEncodingException("No certificate contained in the cert path.");
+ }
+ return setBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
+ certPath.getEncoded(CERT_PATH_ENCODING));
+ }
+
+ /**
* Returns the list of recovery agents initialized for given {@code userId}
* @param userId The userId of the profile the application is running under.
* @return The list of recovery agents
@@ -515,48 +594,6 @@
}
/**
- * Returns the first (and only?) public key for {@code userId}.
- *
- * @param userId The userId of the profile whose keys are to be synced.
- * @return The public key, or null if none exists.
- */
- @Nullable
- public PublicKey getRecoveryServicePublicKey(int userId) {
- SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
-
- String[] projection = { RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY };
- String selection =
- RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
- String[] selectionArguments = { Integer.toString(userId) };
-
- try (
- Cursor cursor = db.query(
- RecoveryServiceMetadataEntry.TABLE_NAME,
- projection,
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
- ) {
- if (cursor.getCount() < 1) {
- return null;
- }
-
- cursor.moveToFirst();
- byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow(
- RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY));
-
- try {
- return decodeX509Key(keyBytes);
- } catch (InvalidKeySpecException e) {
- Log.wtf(TAG, "Could not decode public key for " + userId);
- return null;
- }
- }
- }
-
- /**
* Updates the counterId
*
* @param userId The userId of the profile the application is running under.
@@ -585,7 +622,6 @@
return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID);
}
-
/**
* Updates the server parameters given by the application initializing the local recovery
* components.
@@ -869,4 +905,16 @@
throw new RuntimeException(e);
}
}
+
+ @Nullable
+ private static CertPath decodeCertPath(byte[] bytes) throws CertificateException {
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ // Should not happen, as X.509 is mandatory for all providers.
+ throw new RuntimeException(e);
+ }
+ return certFactory.generateCertPath(new ByteArrayInputStream(bytes), CERT_PATH_ENCODING);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 4ee282b..1cb5d91 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -104,9 +104,10 @@
static final String COLUMN_NAME_UID = "uid";
/**
- * Version of the latest recovery snapshot
+ * Version of the latest recovery snapshot.
*/
static final String COLUMN_NAME_SNAPSHOT_VERSION = "snapshot_version";
+
/**
* Flag to generate new snapshot.
*/
@@ -118,6 +119,16 @@
static final String COLUMN_NAME_PUBLIC_KEY = "public_key";
/**
+ * The certificate path of the recovery service.
+ */
+ static final String COLUMN_NAME_CERT_PATH = "cert_path";
+
+ /**
+ * The serial number contained in the certificate XML file of the recovery service.
+ */
+ static final String COLUMN_NAME_CERT_SERIAL = "cert_serial";
+
+ /**
* Secret types used for end-to-end encryption.
*/
static final String COLUMN_NAME_SECRET_TYPES = "secret_types";
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index d96671c..8a89f2d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
@@ -28,7 +29,9 @@
* Helper for creating the recoverable key database.
*/
class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
- private static final int DATABASE_VERSION = 1;
+ private static final String TAG = "RecoverableKeyStoreDbHp";
+
+ static final int DATABASE_VERSION = 3;
private static final String DATABASE_NAME = "recoverablekeystore.db";
private static final String SQL_CREATE_KEYS_ENTRY =
@@ -59,6 +62,8 @@
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
@@ -88,9 +93,39 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL(SQL_DELETE_KEYS_ENTRY);
- db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
- db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
- onCreate(db);
+ if (oldVersion < 2) {
+ db.execSQL(SQL_DELETE_KEYS_ENTRY);
+ db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
+ db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
+ onCreate(db);
+ return;
+ }
+
+ if (oldVersion < 3) {
+ upgradeDbForVersion3(db);
+ }
+ }
+
+ private void upgradeDbForVersion3(SQLiteDatabase db) {
+ // Add the two columns for cert path and cert serial number
+ addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, "BLOB", /*defaultStr=*/ null);
+ addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, "INTEGER", /*defaultStr=*/
+ null);
+ }
+
+ private static void addColumnToTable(
+ SQLiteDatabase db, String tableName, String column, String columnType,
+ String defaultStr) {
+ Log.d(TAG, "Adding column " + column + " to " + tableName + ".");
+
+ String alterStr = "ALTER TABLE " + tableName + " ADD COLUMN " + column + " " + columnType;
+ if (defaultStr != null && !defaultStr.isEmpty()) {
+ alterStr += " DEFAULT " + defaultStr;
+ }
+
+ db.execSQL(alterStr + ";");
}
}
+
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index 7881a95..648c782 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -289,6 +289,7 @@
}
}
+ @GuardedBy("mLock")
private void sendAudioPlayerActiveStateChangedMessageLocked(
final AudioPlaybackConfiguration config, final boolean isRemoved) {
for (MessageHandler messageHandler : mListenerMap.values()) {
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 4d56468..e3e02e3 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -24,11 +24,11 @@
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.net.DelayedDiskWrite;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
@@ -38,8 +38,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.net.InetAddress;
import java.net.Inet4Address;
+import java.net.InetAddress;
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
@@ -60,7 +60,7 @@
protected static final String EXCLUSION_LIST_KEY = "exclusionList";
protected static final String EOS = "eos";
- protected static final int IPCONFIG_FILE_VERSION = 2;
+ protected static final int IPCONFIG_FILE_VERSION = 3;
public IpConfigStore(DelayedDiskWrite writer) {
mWriter = writer;
@@ -70,9 +70,14 @@
this(new DelayedDiskWrite());
}
+ private static boolean writeConfig(DataOutputStream out, String configKey,
+ IpConfiguration config) throws IOException {
+ return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION);
+ }
+
@VisibleForTesting
- public static boolean writeConfig(DataOutputStream out, int configKey,
- IpConfiguration config) throws IOException {
+ public static boolean writeConfig(DataOutputStream out, String configKey,
+ IpConfiguration config, int version) throws IOException {
boolean written = false;
try {
@@ -153,7 +158,11 @@
if (written) {
out.writeUTF(ID_KEY);
- out.writeInt(configKey);
+ if (version < 3) {
+ out.writeInt(Integer.valueOf(configKey));
+ } else {
+ out.writeUTF(configKey);
+ }
}
} catch (NullPointerException e) {
loge("Failure in writing " + config + e);
@@ -163,18 +172,47 @@
return written;
}
- public void writeIpAndProxyConfigurations(String filePath,
+ /**
+ * @Deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
+ * New method uses string as network identifier which could be interface name or MAC address or
+ * other token.
+ */
+ @Deprecated
+ public void writeIpAndProxyConfigurationsToFile(String filePath,
final SparseArray<IpConfiguration> networks) {
- mWriter.write(filePath, new DelayedDiskWrite.Writer() {
- public void onWriteCalled(DataOutputStream out) throws IOException{
- out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
- writeConfig(out, networks.keyAt(i), networks.valueAt(i));
- }
+ mWriter.write(filePath, out -> {
+ out.writeInt(IPCONFIG_FILE_VERSION);
+ for(int i = 0; i < networks.size(); i++) {
+ writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i));
}
});
}
+ public void writeIpConfigurations(String filePath,
+ ArrayMap<String, IpConfiguration> networks) {
+ mWriter.write(filePath, out -> {
+ out.writeInt(IPCONFIG_FILE_VERSION);
+ for(int i = 0; i < networks.size(); i++) {
+ writeConfig(out, networks.keyAt(i), networks.valueAt(i));
+ }
+ });
+ }
+
+ public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) {
+ BufferedInputStream bufferedInputStream;
+ try {
+ bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
+ } catch (FileNotFoundException e) {
+ // Return an empty array here because callers expect an empty array when the file is
+ // not present.
+ loge("Error opening configuration file: " + e);
+ return new ArrayMap<>(0);
+ }
+ return readIpConfigurations(bufferedInputStream);
+ }
+
+ /** @Deprecated use {@link #readIpConfigurations(String)} */
+ @Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
try {
@@ -188,21 +226,40 @@
return readIpAndProxyConfigurations(bufferedInputStream);
}
+ /** @Deprecated use {@link #readIpConfigurations(InputStream)} */
+ @Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
InputStream inputStream) {
- SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
+ ArrayMap<String, IpConfiguration> networks = readIpConfigurations(inputStream);
+ if (networks == null) {
+ return null;
+ }
+
+ SparseArray<IpConfiguration> networksById = new SparseArray<>();
+ for (int i = 0; i < networks.size(); i++) {
+ int id = Integer.valueOf(networks.keyAt(i));
+ networksById.put(id, networks.valueAt(i));
+ }
+
+ return networksById;
+ }
+
+ /** Returns a map of network identity token and {@link IpConfiguration}. */
+ public static ArrayMap<String, IpConfiguration> readIpConfigurations(
+ InputStream inputStream) {
+ ArrayMap<String, IpConfiguration> networks = new ArrayMap<>();
DataInputStream in = null;
try {
in = new DataInputStream(inputStream);
int version = in.readInt();
- if (version != 2 && version != 1) {
+ if (version != 3 && version != 2 && version != 1) {
loge("Bad version on IP configuration file, ignore read");
return null;
}
while (true) {
- int id = -1;
+ String uniqueToken = null;
// Default is DHCP with no proxy
IpAssignment ipAssignment = IpAssignment.DHCP;
ProxySettings proxySettings = ProxySettings.NONE;
@@ -217,7 +274,12 @@
key = in.readUTF();
try {
if (key.equals(ID_KEY)) {
- id = in.readInt();
+ if (version < 3) {
+ int id = in.readInt();
+ uniqueToken = String.valueOf(id);
+ } else {
+ uniqueToken = in.readUTF();
+ }
} else if (key.equals(IP_ASSIGNMENT_KEY)) {
ipAssignment = IpAssignment.valueOf(in.readUTF());
} else if (key.equals(LINK_ADDRESS_KEY)) {
@@ -280,9 +342,9 @@
}
} while (true);
- if (id != -1) {
+ if (uniqueToken != null) {
IpConfiguration config = new IpConfiguration();
- networks.put(id, config);
+ networks.put(uniqueToken, config);
switch (ipAssignment) {
case STATIC:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f09de52..f346629 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -106,9 +106,9 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityManager;
-import android.app.INotificationManager;
import android.app.IUidObserver;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -175,6 +175,7 @@
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -208,10 +209,8 @@
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
-import com.android.server.SystemService;
import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -332,6 +331,8 @@
"com.android.server.net.action.ALLOW_BACKGROUND";
private static final String ACTION_SNOOZE_WARNING =
"com.android.server.net.action.SNOOZE_WARNING";
+ private static final String ACTION_SNOOZE_RAPID =
+ "com.android.server.net.action.SNOOZE_RAPID";
private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
@@ -365,7 +366,6 @@
private final CarrierConfigManager mCarrierConfigManager;
private IConnectivityManager mConnManager;
- private INotificationManager mNotifManager;
private PowerManagerInternal mPowerManagerInternal;
private IDeviceIdleController mDeviceIdleController;
@GuardedBy("mUidRulesFirstLock")
@@ -564,10 +564,6 @@
mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
}
- public void bindNotificationManager(INotificationManager notifManager) {
- mNotifManager = checkNotNull(notifManager, "missing INotificationManager");
- }
-
void updatePowerSaveWhitelistUL() {
try {
int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
@@ -772,10 +768,11 @@
final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler);
- // listen for snooze warning from notifications
- final IntentFilter snoozeWarningFilter = new IntentFilter(ACTION_SNOOZE_WARNING);
- mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter,
- MANAGE_NETWORK_POLICY, mHandler);
+ // Listen for snooze from notifications
+ mContext.registerReceiver(mSnoozeReceiver,
+ new IntentFilter(ACTION_SNOOZE_WARNING), MANAGE_NETWORK_POLICY, mHandler);
+ mContext.registerReceiver(mSnoozeReceiver,
+ new IntentFilter(ACTION_SNOOZE_RAPID), MANAGE_NETWORK_POLICY, mHandler);
// listen for configured wifi networks to be loaded
final IntentFilter wifiFilter =
@@ -960,14 +957,18 @@
* Receiver that watches for {@link Notification} control of
* {@link NetworkPolicy#lastWarningSnooze}.
*/
- final private BroadcastReceiver mSnoozeWarningReceiver = new BroadcastReceiver() {
+ final private BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified MANAGE_NETWORK_POLICY
// permission above.
final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
- performSnooze(template, TYPE_WARNING);
+ if (ACTION_SNOOZE_WARNING.equals(intent.getAction())) {
+ performSnooze(template, TYPE_WARNING);
+ } else if (ACTION_SNOOZE_RAPID.equals(intent.getAction())) {
+ performSnooze(template, TYPE_RAPID);
+ }
}
};
@@ -1025,13 +1026,6 @@
}
};
- @VisibleForTesting
- public void updateNotifications() {
- synchronized (mNetworkPoliciesSecondLock) {
- updateNotificationsNL();
- }
- }
-
/**
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
@@ -1047,6 +1041,7 @@
// cycle boundary to recompute notifications.
// examine stats for each active policy
+ final long now = currentTimeMillis();
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// ignore policies that aren't relevant to user
@@ -1055,12 +1050,14 @@
final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
.cycleIterator(policy).next();
- final long start = cycle.first.toInstant().toEpochMilli();
- final long end = cycle.second.toInstant().toEpochMilli();
- final long totalBytes = getTotalBytes(policy.template, start, end);
+ final long cycleStart = cycle.first.toInstant().toEpochMilli();
+ final long cycleEnd = cycle.second.toInstant().toEpochMilli();
+ final long totalBytes = getTotalBytes(policy.template, cycleStart, cycleEnd);
+ // Notify when data usage is over warning/limit
if (policy.isOverLimit(totalBytes)) {
- if (policy.lastLimitSnooze >= start) {
+ final boolean snoozedThisCycle = policy.lastLimitSnooze >= cycleStart;
+ if (snoozedThisCycle) {
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
enqueueNotification(policy, TYPE_LIMIT, totalBytes);
@@ -1070,45 +1067,30 @@
} else {
notifyUnderLimitNL(policy.template);
- if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
+ final boolean snoozedThisCycle = policy.lastWarningSnooze >= cycleStart;
+ if (policy.isOverWarning(totalBytes) && !snoozedThisCycle) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
}
}
- }
- // Alert the user about heavy recent data usage that might result in
- // going over their carrier limit.
- for (int i = 0; i < mNetIdToSubId.size(); i++) {
- final int subId = mNetIdToSubId.valueAt(i);
- final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
- if (plan == null) continue;
-
- final long limitBytes = plan.getDataLimitBytes();
- if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) {
- // Ignore missing limits
- } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) {
- // Unlimited data; no rapid usage alerting
- } else {
- // Warn if average usage over last 4 days is on track to blow
- // pretty far past the plan limits.
+ // Warn if average usage over last 4 days is on track to blow pretty
+ // far past the plan limits.
+ if (policy.limitBytes != LIMIT_DISABLED) {
final long recentDuration = TimeUnit.DAYS.toMillis(4);
- final long end = RecurrenceRule.sClock.millis();
- final long start = end - recentDuration;
+ final long recentBytes = getTotalBytes(policy.template, now - recentDuration, now);
- final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(
- mContext.getSystemService(TelephonyManager.class).getSubscriberId(subId));
- final long recentBytes = getTotalBytes(template, start, end);
-
- final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next();
- final long cycleDuration = cycle.second.toInstant().toEpochMilli()
- - cycle.first.toInstant().toEpochMilli();
-
+ final long cycleDuration = cycleEnd - cycleStart;
final long projectedBytes = (recentBytes * cycleDuration) / recentDuration;
- final long alertBytes = (limitBytes * 3) / 2;
- if (projectedBytes > alertBytes) {
- final NetworkPolicy policy = new NetworkPolicy(template, plan.getCycleRule(),
- NetworkPolicy.WARNING_DISABLED, NetworkPolicy.LIMIT_DISABLED,
- NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER, true, true);
+ final long alertBytes = (policy.limitBytes * 3) / 2;
+
+ if (LOGD) {
+ Slog.d(TAG, "Rapid usage considering recent " + recentBytes + " projected "
+ + projectedBytes + " alert " + alertBytes);
+ }
+
+ final boolean snoozedRecently = policy.lastRapidSnooze >= now
+ - DateUtils.DAY_IN_MILLIS;
+ if (projectedBytes > alertBytes && !snoozedRecently) {
enqueueNotification(policy, TYPE_RAPID, 0);
}
}
@@ -1131,8 +1113,8 @@
*/
private boolean isTemplateRelevant(NetworkTemplate template) {
if (template.isMatchRuleMobile()) {
- final TelephonyManager tele = TelephonyManager.from(mContext);
- final SubscriptionManager sub = SubscriptionManager.from(mContext);
+ final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+ final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
// Mobile template is relevant when any active subscriber matches
final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
@@ -1173,7 +1155,7 @@
private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) {
final NotificationId notificationId = new NotificationId(policy, type);
final Notification.Builder builder =
- new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS);
+ new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_ALERTS);
builder.setOnlyAlertOnce(true);
builder.setWhen(0L);
builder.setColor(mContext.getColor(
@@ -1190,8 +1172,6 @@
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
- builder.setDefaults(Notification.DEFAULT_ALL);
- builder.setChannelId(SystemNotificationChannels.NETWORK_ALERTS);
final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
builder.setDeleteIntent(PendingIntent.getBroadcast(
@@ -1267,6 +1247,7 @@
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
+ builder.setChannelId(SystemNotificationChannels.NETWORK_STATUS);
final Intent intent = buildViewDataUsageIntent(res, policy.template);
builder.setContentIntent(PendingIntent.getActivity(
@@ -1277,45 +1258,34 @@
final CharSequence title = res.getText(R.string.data_usage_rapid_title);
body = res.getText(R.string.data_usage_rapid_body);
- builder.setOngoing(true);
builder.setSmallIcon(R.drawable.stat_notify_error);
builder.setTicker(title);
builder.setContentTitle(title);
builder.setContentText(body);
- final Intent intent = buildViewDataUsageIntent(res, policy.template);
+ final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template);
+ builder.setDeleteIntent(PendingIntent.getBroadcast(
+ mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
+ final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
break;
}
}
- // TODO: move to NotificationManager once we can mock it
- try {
- final String packageName = mContext.getPackageName();
- if (!TextUtils.isEmpty(body)) {
- builder.setStyle(new Notification.BigTextStyle()
- .bigText(body));
- }
- mNotifManager.enqueueNotificationWithTag(
- packageName, packageName, notificationId.getTag(), notificationId.getId(),
- builder.build(), UserHandle.USER_ALL);
- mActiveNotifs.add(notificationId);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
+ if (!TextUtils.isEmpty(body)) {
+ builder.setStyle(new Notification.BigTextStyle().bigText(body));
}
+
+ mContext.getSystemService(NotificationManager.class).notifyAsUser(notificationId.getTag(),
+ notificationId.getId(), builder.build(), UserHandle.ALL);
+ mActiveNotifs.add(notificationId);
}
private void cancelNotification(NotificationId notificationId) {
- // TODO: move to NotificationManager once we can mock it
- try {
- final String packageName = mContext.getPackageName();
- mNotifManager.cancelNotificationWithTag(
- packageName, notificationId.getTag(), notificationId.getId(),
- UserHandle.USER_ALL);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- }
+ mContext.getSystemService(NotificationManager.class).cancel(notificationId.getTag(),
+ notificationId.getId());
}
/**
@@ -1342,8 +1312,13 @@
};
@VisibleForTesting
- public void updateNetworks() {
+ public void updateNetworks() throws InterruptedException {
mConnReceiver.onReceive(null, null);
+ final CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ latch.countDown();
+ });
+ latch.await(5, TimeUnit.SECONDS);
}
/**
@@ -1357,7 +1332,8 @@
if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
boolean policyUpdated = false;
- final String subscriberId = TelephonyManager.from(mContext).getSubscriberId(subId);
+ final String subscriberId = mContext.getSystemService(TelephonyManager.class)
+ .getSubscriberId(subId);
// find and update the mobile NetworkPolicy for this subscriber id
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
@@ -1482,7 +1458,7 @@
return;
}
final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, -1);
- final TelephonyManager tele = TelephonyManager.from(mContext);
+ final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
final String subscriberId = tele.getSubscriberId(subId);
maybeRefreshTrustedTime();
@@ -1561,8 +1537,8 @@
if (template.getMatchRule() == MATCH_MOBILE_ALL) {
// If mobile data usage hits the limit or if the user resumes the data, we need to
// notify telephony.
- final SubscriptionManager sm = SubscriptionManager.from(mContext);
- final TelephonyManager tm = TelephonyManager.from(mContext);
+ final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+ final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList());
for (int subId : subIds) {
@@ -1746,7 +1722,7 @@
final long totalBytes = getTotalBytes(
NetworkTemplate.buildTemplateMobileAll(state.subscriberId), start, end);
final long remainingBytes = limitBytes - totalBytes;
- final long remainingDays = Math.min(1, (end - RecurrenceRule.sClock.millis())
+ final long remainingDays = Math.max(1, (end - currentTimeMillis())
/ TimeUnit.DAYS.toMillis(1));
if (remainingBytes > 0) {
quotaBytes = (remainingBytes / remainingDays) / 10;
@@ -1770,8 +1746,8 @@
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
if (mSuppressDefaultPolicy) return;
- final TelephonyManager tele = TelephonyManager.from(mContext);
- final SubscriptionManager sub = SubscriptionManager.from(mContext);
+ final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+ final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
for (int subId : subIds) {
@@ -2516,7 +2492,7 @@
}
private void normalizePoliciesNL(NetworkPolicy[] policies) {
- final TelephonyManager tele = TelephonyManager.from(mContext);
+ final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
final String[] merged = tele.getMergedSubscriberIds();
mNetworkPolicy.clear();
@@ -2564,6 +2540,9 @@
case TYPE_LIMIT:
policy.lastLimitSnooze = currentTime;
break;
+ case TYPE_RAPID:
+ policy.lastRapidSnooze = currentTime;
+ break;
default:
throw new IllegalArgumentException("unexpected type");
}
@@ -4425,6 +4404,14 @@
private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ return intent;
+ }
+
+ private static Intent buildSnoozeRapidIntent(NetworkTemplate template) {
+ final Intent intent = new Intent(ACTION_SNOOZE_RAPID);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
return intent;
}
@@ -4691,10 +4678,12 @@
return subId;
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private int getSubIdLocked(Network network) {
return mNetIdToSubId.get(network.netId, INVALID_SUBSCRIPTION_ID);
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private SubscriptionPlan getPrimarySubscriptionPlanLocked(int subId) {
final SubscriptionPlan[] plans = mSubscriptionPlans.get(subId);
return ArrayUtils.isEmpty(plans) ? null : plans[0];
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index bfc150e..76c4db1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -405,6 +405,7 @@
mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags);
}
+ @GuardedBy("mStatsLock")
private void shutdownLocked() {
mContext.unregisterReceiver(mTetherReceiver);
mContext.unregisterReceiver(mPollReceiver);
@@ -431,6 +432,7 @@
mSystemReady = false;
}
+ @GuardedBy("mStatsLock")
private void maybeUpgradeLegacyStatsLocked() {
File file;
try {
@@ -909,6 +911,7 @@
* reflect current {@link #mPersistThreshold} value. Always defers to
* {@link Global} values when defined.
*/
+ @GuardedBy("mStatsLock")
private void updatePersistThresholdsLocked() {
mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold));
mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold));
@@ -1029,6 +1032,7 @@
* are active on a single {@code iface}, they are combined under a single
* {@link NetworkIdentitySet}.
*/
+ @GuardedBy("mStatsLock")
private void updateIfacesLocked(Network[] defaultNetworks) {
if (!mSystemReady) return;
if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
@@ -1128,6 +1132,7 @@
return ident;
}
+ @GuardedBy("mStatsLock")
private void recordSnapshotLocked(long currentTime) throws RemoteException {
// snapshot and record current counters; read UID stats first to
// avoid over counting dev stats.
@@ -1163,6 +1168,7 @@
* Bootstrap initial stats snapshot, usually during {@link #systemReady()}
* so we have baseline values without double-counting.
*/
+ @GuardedBy("mStatsLock")
private void bootstrapStatsLocked() {
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
@@ -1197,6 +1203,7 @@
* Periodic poll operation, reading current statistics and recording into
* {@link NetworkStatsHistory}.
*/
+ @GuardedBy("mStatsLock")
private void performPollLocked(int flags) {
if (!mSystemReady) return;
if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
@@ -1258,6 +1265,7 @@
/**
* Sample recent statistics summary into {@link EventLog}.
*/
+ @GuardedBy("mStatsLock")
private void performSampleLocked() {
// TODO: migrate trustedtime fixes to separate binary log events
final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1;
@@ -1295,6 +1303,7 @@
/**
* Clean up {@link #mUidRecorder} after UID is removed.
*/
+ @GuardedBy("mStatsLock")
private void removeUidsLocked(int... uids) {
if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
@@ -1313,6 +1322,7 @@
/**
* Clean up {@link #mUidRecorder} after user is removed.
*/
+ @GuardedBy("mStatsLock")
private void removeUserLocked(int userId) {
if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
@@ -1434,6 +1444,7 @@
}
}
+ @GuardedBy("mStatsLock")
private void dumpProtoLocked(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 5d4287b..64dc98e 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
jsharkey@android.com
lorenzo@google.com
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 5f4e471..e5fc6e5 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -63,7 +63,7 @@
@Override
public void onStart() {
if (Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.NETWORK_WATCHLIST_ENABLED, 0) == 0) {
+ Settings.Global.NETWORK_WATCHLIST_ENABLED, 1) == 0) {
// Watchlist service is disabled
Slog.i(TAG, "Network Watchlist service is disabled");
return;
@@ -76,7 +76,7 @@
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
if (Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.NETWORK_WATCHLIST_ENABLED, 0) == 0) {
+ Settings.Global.NETWORK_WATCHLIST_ENABLED, 1) == 0) {
// Watchlist service is disabled
Slog.i(TAG, "Network Watchlist service is disabled");
return;
diff --git a/services/core/java/com/android/server/net/watchlist/ReportEncoder.java b/services/core/java/com/android/server/net/watchlist/ReportEncoder.java
index 5d7ff5a..2a8f4d5 100644
--- a/services/core/java/com/android/server/net/watchlist/ReportEncoder.java
+++ b/services/core/java/com/android/server/net/watchlist/ReportEncoder.java
@@ -19,38 +19,31 @@
import android.annotation.Nullable;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.service.NetworkWatchlistReportProto;
+import com.android.service.NetworkWatchlistAppResultProto;
+import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
- * Helper class to encode and generate serialized DP encoded watchlist report.
- *
- * <p>Serialized report data structure:
- * [4 bytes magic number][4_bytes_report_version_code][32_bytes_watchlist_hash]
- * [app_1_digest_byte_array][app_1_encoded_visited_cnc_byte]
- * [app_2_digest_byte_array][app_2_encoded_visited_cnc_byte]
- * ...
- *
- * Total size: 4 + 4 + 32 + (32+1)*N, where N = number of digests
+ * Helper class to encode and generate serialized DP encoded watchlist proto report.
*/
class ReportEncoder {
private static final String TAG = "ReportEncoder";
- // Report header magic number
- private static final byte[] MAGIC_NUMBER = {(byte) 0x8D, (byte) 0x37, (byte) 0x0A, (byte) 0xAC};
// Report version number, as file format / parameters can be changed in later version, we need
// to have versioning on watchlist report format
- private static final byte[] REPORT_VERSION = {(byte) 0x00, (byte) 0x01};
+ private static final int REPORT_VERSION = 1;
private static final int WATCHLIST_HASH_SIZE = 32;
- private static final int APP_DIGEST_SIZE = 32;
/**
* Apply DP on watchlist results, and generate a serialized watchlist report ready to store
@@ -64,11 +57,10 @@
}
/**
- * Convert DP encoded watchlist report into byte[] format.
- * TODO: Serialize it using protobuf
+ * Convert DP encoded watchlist report into proto format.
*
* @param encodedReportMap DP encoded watchlist report.
- * @return Watchlist report in byte[] format, which will be shared in Dropbox. Null if
+ * @return Watchlist report in proto format, which will be shared in Dropbox. Null if
* watchlist report cannot be generated.
*/
@Nullable
@@ -85,42 +77,25 @@
Log.e(TAG, "Unexpected hash length");
return null;
}
- final int reportMapSize = encodedReportMap.size();
- final byte[] outputReport =
- new byte[MAGIC_NUMBER.length + REPORT_VERSION.length + WATCHLIST_HASH_SIZE
- + reportMapSize * (APP_DIGEST_SIZE + /* Result */ 1)];
- final List<String> sortedKeys = new ArrayList(encodedReportMap.keySet());
- Collections.sort(sortedKeys);
-
- int offset = 0;
-
- // Set magic number to report
- System.arraycopy(MAGIC_NUMBER, 0, outputReport, offset, MAGIC_NUMBER.length);
- offset += MAGIC_NUMBER.length;
+ final ByteArrayOutputStream reportOutputStream = new ByteArrayOutputStream();
+ final ProtoOutputStream proto = new ProtoOutputStream(reportOutputStream);
// Set report version to report
- System.arraycopy(REPORT_VERSION, 0, outputReport, offset, REPORT_VERSION.length);
- offset += REPORT_VERSION.length;
-
- // Set watchlist hash to report
- System.arraycopy(watchlistHash, 0, outputReport, offset, watchlistHash.length);
- offset += watchlistHash.length;
+ proto.write(NetworkWatchlistReportProto.REPORT_VERSION, REPORT_VERSION);
+ proto.write(NetworkWatchlistReportProto.WATCHLIST_CONFIG_HASH,
+ HexDump.toHexString(watchlistHash));
// Set app digest, encoded_isPha pair to report
- for (int i = 0; i < reportMapSize; i++) {
- String key = sortedKeys.get(i);
+ for (Map.Entry<String, Boolean> entry : encodedReportMap.entrySet()) {
+ String key = entry.getKey();
byte[] digest = HexDump.hexStringToByteArray(key);
- boolean isPha = encodedReportMap.get(key);
- System.arraycopy(digest, 0, outputReport, offset, APP_DIGEST_SIZE);
- offset += digest.length;
- outputReport[offset] = (byte) (isPha ? 1 : 0);
- offset += 1;
+ boolean encodedResult = entry.getValue();
+ long token = proto.start(NetworkWatchlistReportProto.APP_RESULT);
+ proto.write(NetworkWatchlistAppResultProto.APP_DIGEST, key);
+ proto.write(NetworkWatchlistAppResultProto.ENCODED_RESULT, encodedResult);
+ proto.end(token);
}
- if (outputReport.length != offset) {
- Log.e(TAG, "Watchlist report size does not match! Offset: " + offset + ", report size: "
- + outputReport.length);
-
- }
- return outputReport;
+ proto.flush();
+ return reportOutputStream.toByteArray();
}
}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index c4de4ac..81fe641 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -128,7 +128,7 @@
Slog.e(TAG, "Couldn't find package: " + packageNames);
return false;
}
- ai = mPm.getApplicationInfo(packageNames[0],0);
+ ai = mPm.getApplicationInfo(packageNames[0], 0);
} catch (NameNotFoundException e) {
// Should not happen.
return false;
@@ -136,7 +136,7 @@
return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
}
- /**
+ /**
* Report network watchlist records if we collected enough data.
*/
public void reportWatchlistIfNecessary() {
@@ -180,6 +180,10 @@
return true;
}
final byte[] digest = getDigestFromUid(uid);
+ if (digest == null) {
+ Slog.e(TAG, "Cannot get digest from uid: " + uid);
+ return false;
+ }
final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
tryAggregateRecords();
return result;
@@ -242,6 +246,11 @@
final int size = apps.size();
for (int i = 0; i < size; i++) {
byte[] digest = getDigestFromUid(apps.get(i).uid);
+ if (digest == null) {
+ Slog.e(TAG, "Cannot get digest from uid: " + apps.get(i).uid
+ + ",pkg: " + apps.get(i).packageName);
+ continue;
+ }
result.add(HexDump.toHexString(digest));
}
// Step 2: Add all digests from records
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 502760a..b7842d5 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -376,9 +376,7 @@
protected void upgradeXml(final int xmlVersion, final int userId) {}
private void loadAllowedComponentsFromSettings() {
-
- UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- for (UserInfo user : userManager.getUsers()) {
+ for (UserInfo user : mUm.getUsers()) {
final ContentResolver cr = mContext.getContentResolver();
addApprovedList(Settings.Secure.getStringForUser(
cr,
@@ -482,7 +480,9 @@
for (int i = 0; i < allowedByType.size(); i++) {
final ArraySet<String> allowed = allowedByType.valueAt(i);
allowedPackages.addAll(
- allowed.stream().map(this::getPackageName).collect(Collectors.toList()));
+ allowed.stream().map(this::getPackageName).
+ filter(value -> !TextUtils.isEmpty(value))
+ .collect(Collectors.toList()));
}
return allowedPackages;
}
@@ -771,7 +771,7 @@
* Called whenever packages change, the user switches, or the secure setting
* is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
*/
- private void rebindServices(boolean forceRebind) {
+ protected void rebindServices(boolean forceRebind) {
if (DEBUG) Slog.d(TAG, "rebindServices");
final int[] userIds = mUserProfiles.getCurrentProfileIds();
final int nUserIds = userIds.length;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 727e7ee..3800017 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -25,7 +25,9 @@
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService
.HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
@@ -88,6 +90,7 @@
import android.companion.ICompanionDeviceManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -115,6 +118,7 @@
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -206,6 +210,7 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -288,6 +293,7 @@
private ICompanionDeviceManager mCompanionManager;
private AccessibilityManager mAccessibilityManager;
private IDeviceIdleController mDeviceIdleController;
+ private IBinder mPermissionOwner;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -524,7 +530,7 @@
} catch (FileNotFoundException e) {
// No data yet
// Load default managed services approvals
- readDefaultApprovedServices(UserHandle.USER_SYSTEM);
+ readDefaultApprovedServices(USER_SYSTEM);
} catch (IOException e) {
Log.wtf(TAG, "Unable to read notification policy", e);
} catch (NumberFormatException e) {
@@ -680,6 +686,7 @@
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
REASON_CLICK, null);
+ reportUserInteraction(r);
}
}
@@ -700,7 +707,7 @@
.setSubtype(actionIndex));
EventLogTags.writeNotificationActionClicked(key, actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
- // TODO: Log action click via UsageStats.
+ reportUserInteraction(r);
}
}
@@ -821,6 +828,7 @@
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.recordDirectReplied();
+ reportUserInteraction(r);
}
}
}
@@ -974,7 +982,7 @@
final int enabled = mPackageManager.getApplicationEnabledSetting(
pkgName,
changeUserId != UserHandle.USER_ALL ? changeUserId :
- UserHandle.USER_SYSTEM);
+ USER_SYSTEM);
if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|| enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
cancelNotifications = false;
@@ -1061,6 +1069,7 @@
}
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ mUserProfiles.updateCache(context);
mZenModeHelper.onUserRemoved(user);
mRankingHelper.onUserRemoved(user);
mListeners.onUserRemoved(user);
@@ -1268,7 +1277,7 @@
NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
NotificationUsageStats usageStats, AtomicFile policyFile,
- ActivityManager activityManager, GroupHelper groupHelper) {
+ ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am) {
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1276,7 +1285,7 @@
mAccessibilityManager =
(AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- mAm = ActivityManager.getService();
+ mAm = am;
mPackageManager = packageManager;
mPackageManagerClient = packageManagerClient;
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -1287,6 +1296,11 @@
mActivityManager = activityManager;
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ try {
+ mPermissionOwner = mAm.newUriPermissionOwner("notification");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "AM dead", e);
+ }
mHandler = new WorkerHandler(looper);
mRankingThread.start();
@@ -1415,7 +1429,7 @@
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"),
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
- getGroupHelper());
+ getGroupHelper(), ActivityManager.getService());
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -1593,7 +1607,7 @@
&& update.getImportance() == IMPORTANCE_NONE)) {
getContext().sendBroadcastAsUser(
new Intent(ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED)
- .putExtra(NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID,
+ .putExtra(NotificationManager.EXTRA_NOTIFICATION_CHANNEL_ID,
update.getId())
.putExtra(NotificationManager.EXTRA_BLOCKED_STATE,
update.getImportance() == IMPORTANCE_NONE)
@@ -1631,7 +1645,7 @@
if (preUpdate.isBlocked() != update.isBlocked()) {
getContext().sendBroadcastAsUser(
new Intent(ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED)
- .putExtra(NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID,
+ .putExtra(NotificationManager.EXTRA_NOTIFICATION_CHANNEL_GROUP_ID,
update.getId())
.putExtra(NotificationManager.EXTRA_BLOCKED_STATE,
update.isBlocked())
@@ -1746,14 +1760,29 @@
return INotificationManager.Stub.asInterface(mService);
}
+ /**
+ * Report to usage stats that the notification was seen.
+ * @param r notification record
+ */
protected void reportSeen(NotificationRecord r) {
final int userId = r.sbn.getUserId();
mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
+ userId == UserHandle.USER_ALL ? USER_SYSTEM
: userId,
UsageEvents.Event.NOTIFICATION_SEEN);
}
+ /**
+ * Report to usage stats that the notification was clicked.
+ * @param r notification record
+ */
+ protected void reportUserInteraction(NotificationRecord r) {
+ final int userId = r.sbn.getUserId();
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId,
+ UsageEvents.Event.USER_INTERACTION);
+ }
+
@VisibleForTesting
NotificationManagerInternal getInternalService() {
return mInternalService;
@@ -2891,9 +2920,10 @@
// Backup/restore interface
@Override
public byte[] getBackupPayload(int user) {
+ checkCallerIsSystem();
if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
//TODO: http://b/22388012
- if (user != UserHandle.USER_SYSTEM) {
+ if (user != USER_SYSTEM) {
Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
return null;
}
@@ -2911,6 +2941,7 @@
@Override
public void applyRestore(byte[] payload, int user) {
+ checkCallerIsSystem();
if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
+ (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
if (payload == null) {
@@ -2918,7 +2949,7 @@
return;
}
//TODO: http://b/22388012
- if (user != UserHandle.USER_SYSTEM) {
+ if (user != USER_SYSTEM) {
Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
return;
}
@@ -3676,7 +3707,7 @@
sbn.getNotification().flags =
(r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
mRankingHelper.sort(mNotificationList);
- mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
+ mListeners.notifyPostedLocked(r, sbn /* oldSbn */);
}
};
@@ -3705,7 +3736,7 @@
try {
final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
+ (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
Notification.addFieldsFromContext(ai, notification);
int canColorize = mPackageManagerClient.checkPermission(
@@ -3895,6 +3926,7 @@
return true;
}
+ @GuardedBy("mNotificationLock")
protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
String excludedTag) {
int count = 0;
@@ -4124,6 +4156,8 @@
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ // revoke uri permissions for changed uris
+ revokeUriPermissions(r, old);
r.isUpdate = true;
}
@@ -4145,7 +4179,7 @@
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
- mListeners.notifyPostedLocked(n, oldSbn);
+ mListeners.notifyPostedLocked(r, oldSbn);
if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
mHandler.post(new Runnable() {
@Override
@@ -4910,6 +4944,9 @@
r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER);
}
+ // Revoke permissions
+ revokeUriPermissions(null, r);
+
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
@@ -5007,6 +5044,30 @@
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
}
+ void revokeUriPermissions(NotificationRecord newRecord, NotificationRecord oldRecord) {
+ Set<Uri> oldUris = oldRecord.getNotificationUris();
+ Set<Uri> newUris = newRecord == null ? new HashSet<>() : newRecord.getNotificationUris();
+ oldUris.removeAll(newUris);
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ for (Uri uri : oldUris) {
+ if (uri != null) {
+ int notiUserId = oldRecord.getUserId();
+ int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM
+ : ContentProvider.getUserIdFromUri(uri, notiUserId);
+ uri = ContentProvider.getUriWithoutUserId(uri);
+ mAm.revokeUriPermissionFromOwner(mPermissionOwner,
+ uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Count not revoke uri permissions", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
* and none of the {@code mustNotHaveFlags}.
@@ -5687,6 +5748,12 @@
mListeners.unregisterService(removed.service, removed.userid);
}
+ @Override
+ public void onUserUnlocked(int user) {
+ if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+ rebindServices(true);
+ }
+
public void onNotificationEnqueued(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
@@ -5843,10 +5910,13 @@
* but isn't anymore.
*/
@GuardedBy("mNotificationLock")
- public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
+ public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) {
// Lazily initialized snapshots of the notification.
+ StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
+ Set<Uri> uris = r.getNotificationUris();
+
for (final ManagedServiceInfo info : getServices()) {
boolean sbnVisible = isVisibleToListener(sbn, info);
boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
@@ -5869,6 +5939,9 @@
continue;
}
+ grantUriPermissions(uris, sbn.getUserId(), info.component.getPackageName(),
+ info.userid);
+
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
@@ -5879,6 +5952,28 @@
}
}
+ private void grantUriPermissions(Set<Uri> uris, int notiUserId, String listenerPkg,
+ int listenerUserId) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ for (Uri uri : uris) {
+ if (uri != null) {
+ int sourceUserId = notiUserId == USER_ALL ? USER_SYSTEM
+ : ContentProvider.getUserIdFromUri(uri, notiUserId);
+ uri = ContentProvider.getUriWithoutUserId(uri);
+ mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(),
+ listenerPkg,
+ uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId,
+ listenerUserId == USER_ALL ? USER_SYSTEM : listenerUserId);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Count not grant uri permission to " + listenerPkg, e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* asynchronously notify all listeners about a removed notification
*/
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 23b9743..1ad8c74 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -38,6 +38,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.Adjustment;
@@ -47,6 +48,7 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
@@ -64,6 +66,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Holds data about notifications that should not be shared with the
@@ -929,6 +932,42 @@
mStats.setViewedSettings();
}
+ public Set<Uri> getNotificationUris() {
+ Notification notification = getNotification();
+ Set<Uri> uris = new ArraySet<>();
+
+ if (notification.sound != null) {
+ uris.add(notification.sound);
+ }
+ if (notification.getChannelId() != null) {
+ NotificationChannel channel = getChannel();
+ if (channel != null && channel.getSound() != null) {
+ uris.add(channel.getSound());
+ }
+ }
+ if (notification.extras.containsKey(Notification.EXTRA_AUDIO_CONTENTS_URI)) {
+ uris.add(notification.extras.getParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI));
+ }
+ if (notification.extras.containsKey(Notification.EXTRA_BACKGROUND_IMAGE_URI)) {
+ uris.add(notification.extras.getParcelable(Notification.EXTRA_BACKGROUND_IMAGE_URI));
+ }
+ if (Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+ Parcelable[] newMessages =
+ notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+ List<Notification.MessagingStyle.Message> messages
+ = Notification.MessagingStyle.Message.getMessagesFromBundleArray(newMessages);
+ Parcelable[] histMessages =
+ notification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
+ messages.addAll(
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages));
+ for (Notification.MessagingStyle.Message message : messages) {
+ uris.add(message.getDataUri());
+ }
+ }
+
+ return uris;
+ }
+
public LogMaker getLogMaker(long now) {
if (mLogMaker == null) {
// initialize fields that only change on update (so a new record)
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 7600e81..6e02db7 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -20,6 +20,8 @@
import static android.content.om.OverlayInfo.STATE_ENABLED;
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
+import static android.content.om.OverlayInfo.STATE_OVERLAY_UPGRADING;
+import static android.content.om.OverlayInfo.STATE_TARGET_UPGRADING;
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;
@@ -50,6 +52,10 @@
* @see OverlayManagerService
*/
final class OverlayManagerServiceImpl {
+ // Flags to use in conjunction with updateState.
+ private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
+ private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+
private final PackageManagerHelper mPackageManager;
private final IdmapManager mIdmapManager;
private final OverlayManagerSettings mSettings;
@@ -123,9 +129,7 @@
}
try {
- final PackageInfo targetPackage =
- mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId);
- updateState(targetPackage, overlayPackage, newUserId);
+ updateState(overlayPackage.overlayTarget, overlayPackage.packageName, newUserId, 0);
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
mSettings.remove(overlayPackage.packageName, newUserId);
@@ -168,8 +172,7 @@
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
}
- final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
mListener.onOverlaysChanged(packageName, userId);
}
}
@@ -179,18 +182,18 @@
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
}
- final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
mListener.onOverlaysChanged(packageName, userId);
}
}
void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
if (DEBUG) {
- Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
+ Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId="
+ + userId);
}
- if (updateAllOverlaysForTarget(packageName, userId, null)) {
+ if (updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING)) {
mListener.onOverlaysChanged(packageName, userId);
}
}
@@ -200,8 +203,7 @@
Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
}
- final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
mListener.onOverlaysChanged(packageName, userId);
}
}
@@ -211,7 +213,7 @@
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- if (updateAllOverlaysForTarget(packageName, userId, null)) {
+ if (updateAllOverlaysForTarget(packageName, userId, 0)) {
mListener.onOverlaysChanged(packageName, userId);
}
}
@@ -219,20 +221,21 @@
/**
* Returns true if the settings were modified for this target.
*/
- private boolean updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
- @Nullable final PackageInfo targetPackage) {
+ private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
+ final int userId, final int flags) {
boolean modified = false;
- final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId);
+ final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
final int N = ois.size();
for (int i = 0; i < N; i++) {
final OverlayInfo oi = ois.get(i);
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId);
+ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
+ userId);
if (overlayPackage == null) {
modified |= mSettings.remove(oi.packageName, oi.userId);
removeIdmapIfPossible(oi);
} else {
try {
- modified |= updateState(targetPackage, overlayPackage, userId);
+ modified |= updateState(targetPackageName, oi.packageName, userId, flags);
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
modified |= mSettings.remove(oi.packageName, userId);
@@ -254,14 +257,11 @@
return;
}
- final PackageInfo targetPackage =
- mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId);
-
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
overlayPackage.applicationInfo.getBaseCodePath(),
overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority);
try {
- if (updateState(targetPackage, overlayPackage, userId)) {
+ if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
}
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -271,15 +271,64 @@
}
void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
- Slog.wtf(TAG, "onOverlayPackageChanged called, but only pre-installed overlays supported");
+ if (DEBUG) {
+ Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
+ }
+
+ try {
+ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ if (updateState(oi.targetPackageName, packageName, userId, 0)) {
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ }
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ Slog.e(TAG, "failed to update settings", e);
+ }
}
void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) {
- Slog.wtf(TAG, "onOverlayPackageUpgrading called, but only pre-installed overlays supported");
+ if (DEBUG) {
+ Slog.d(TAG, "onOverlayPackageUpgrading packageName=" + packageName + " userId="
+ + userId);
+ }
+
+ try {
+ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_UPGRADING)) {
+ removeIdmapIfPossible(oi);
+ mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ }
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ Slog.e(TAG, "failed to update settings", e);
+ }
}
void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) {
- Slog.wtf(TAG, "onOverlayPackageUpgraded called, but only pre-installed overlays supported");
+ if (DEBUG) {
+ Slog.d(TAG, "onOverlayPackageUpgraded packageName=" + packageName + " userId="
+ + userId);
+ }
+
+ final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
+ if (pkg == null) {
+ Slog.w(TAG, "overlay package " + packageName + " was upgraded, but couldn't be found");
+ onOverlayPackageRemoved(packageName, userId);
+ return;
+ }
+
+ try {
+ final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
+ if (!oldOi.targetPackageName.equals(pkg.overlayTarget)) {
+ mSettings.init(packageName, userId, pkg.overlayTarget,
+ pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(),
+ pkg.overlayPriority);
+ }
+
+ if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
+ mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+ }
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ Slog.e(TAG, "failed to update settings", e);
+ }
}
void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -333,10 +382,8 @@
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
- final PackageInfo targetPackage =
- mPackageManager.getPackageInfo(oi.targetPackageName, userId);
boolean modified = mSettings.setEnabled(packageName, userId, enable);
- modified |= updateState(targetPackage, overlayPackage, userId);
+ modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
if (modified) {
mListener.onOverlaysChanged(oi.targetPackageName, userId);
@@ -349,7 +396,8 @@
boolean setEnabledExclusive(@NonNull final String packageName, final int userId) {
if (DEBUG) {
- Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, userId));
+ Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName,
+ userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
@@ -359,10 +407,9 @@
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
- final PackageInfo targetPackage =
- mPackageManager.getPackageInfo(oi.targetPackageName, userId);
+ final String targetPackageName = oi.targetPackageName;
- List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
+ List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId);
boolean modified = false;
@@ -384,15 +431,15 @@
// Disable the overlay.
modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
- modified |= updateState(targetPackage, disabledOverlayPackageInfo, userId);
+ modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0);
}
// Enable the selected overlay.
modified |= mSettings.setEnabled(packageName, userId, true);
- modified |= updateState(targetPackage, overlayPackage, userId);
+ modified |= updateState(targetPackageName, packageName, userId, 0);
if (modified) {
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ mListener.onOverlaysChanged(targetPackageName, userId);
}
return true;
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -477,7 +524,8 @@
List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
final int userId) {
- final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId);
+ final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
+ userId);
final List<String> paths = new ArrayList<>(overlays.size());
final int N = overlays.size();
for (int i = 0; i < N; i++) {
@@ -492,36 +540,59 @@
/**
* Returns true if the settings/state was modified, false otherwise.
*/
- private boolean updateState(@Nullable final PackageInfo targetPackage,
- @NonNull final PackageInfo overlayPackage, final int userId)
+ private boolean updateState(@NonNull final String targetPackageName,
+ @NonNull final String overlayPackageName, final int userId, final int flags)
throws OverlayManagerSettings.BadKeyException {
+
+ final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId);
+ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
+ userId);
+
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
- if (targetPackage != null &&
- !("android".equals(targetPackage.packageName)
+ if (targetPackage != null && overlayPackage != null &&
+ !("android".equals(targetPackageName)
&& overlayPackage.isStaticOverlayPackage())) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
- boolean modified = mSettings.setBaseCodePath(overlayPackage.packageName, userId,
- overlayPackage.applicationInfo.getBaseCodePath());
+ boolean modified = false;
+ if (overlayPackage != null) {
+ modified |= mSettings.setBaseCodePath(overlayPackageName, userId,
+ overlayPackage.applicationInfo.getBaseCodePath());
+ }
- final int currentState = mSettings.getState(overlayPackage.packageName, userId);
- final int newState = calculateNewState(targetPackage, overlayPackage, userId);
+ final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId);
+ final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage,
+ userId, flags);
if (currentState != newState) {
if (DEBUG) {
Slog.d(TAG, String.format("%s:%d: %s -> %s",
- overlayPackage.packageName, userId,
+ overlayPackageName, userId,
OverlayInfo.stateToString(currentState),
OverlayInfo.stateToString(newState)));
}
- modified |= mSettings.setState(overlayPackage.packageName, userId, newState);
+ modified |= mSettings.setState(overlayPackageName, userId, newState);
}
return modified;
}
- private int calculateNewState(@Nullable final PackageInfo targetPackage,
- @NonNull final PackageInfo overlayPackage, final int userId)
+ private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
+ @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
throws OverlayManagerSettings.BadKeyException {
+
+ if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
+ return STATE_TARGET_UPGRADING;
+ }
+
+ if ((flags & FLAG_OVERLAY_IS_UPGRADING) != 0) {
+ return STATE_OVERLAY_UPGRADING;
+ }
+
+ // assert expectation on overlay package: can only be null if the flags are used
+ if (DEBUG && overlayPackage == null) {
+ throw new IllegalArgumentException("null overlay package not compatible with no flags");
+ }
+
if (targetPackage == null) {
return STATE_MISSING_TARGET;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 17b38de..a80cae4 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -145,7 +145,8 @@
return mItems.get(idx).setEnabled(enable);
}
- int getState(@NonNull final String packageName, final int userId) throws BadKeyException {
+ @OverlayInfo.State int getState(@NonNull final String packageName, final int userId)
+ throws BadKeyException {
final int idx = select(packageName, userId);
if (idx < 0) {
throw new BadKeyException(packageName, userId);
@@ -156,8 +157,8 @@
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setState(@NonNull final String packageName, final int userId, final int state)
- throws BadKeyException {
+ boolean setState(@NonNull final String packageName, final int userId,
+ final @OverlayInfo.State int state) throws BadKeyException {
final int idx = select(packageName, userId);
if (idx < 0) {
throw new BadKeyException(packageName, userId);
@@ -413,7 +414,7 @@
private final String mPackageName;
private final String mTargetPackageName;
private String mBaseCodePath;
- private int mState;
+ private @OverlayInfo.State int mState;
private boolean mIsEnabled;
private OverlayInfo mCache;
private boolean mIsStatic;
@@ -421,7 +422,7 @@
SettingsItem(@NonNull final String packageName, final int userId,
@NonNull final String targetPackageName, @NonNull final String baseCodePath,
- final int state, final boolean isEnabled, final boolean isStatic,
+ final @OverlayInfo.State int state, final boolean isEnabled, final boolean isStatic,
final int priority) {
mPackageName = packageName;
mUserId = userId;
@@ -462,11 +463,11 @@
return false;
}
- private int getState() {
+ private @OverlayInfo.State int getState() {
return mState;
}
- private boolean setState(final int state) {
+ private boolean setState(final @OverlayInfo.State int state) {
if (mState != state) {
mState = state;
invalidateCache();
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c3f20af..b79caca 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -290,13 +290,14 @@
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
- @Nullable String profileName) throws InstallerException {
+ @Nullable String profileName, @Nullable String dexMetadataPath)
+ throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
- targetSdkVersion, profileName);
+ targetSdkVersion, profileName, dexMetadataPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index af446ba..bc9fa4b 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -126,17 +126,16 @@
final Intent origIntent = requestObj.origIntent;
final Intent sanitizedIntent = sanitizeIntent(origIntent);
- final InstantAppDigest digest = getInstantAppDigest(origIntent);
- final int[] shaPrefix = digest.getDigestPrefix();
AuxiliaryResolveInfo resolveInfo = null;
@ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
try {
final List<InstantAppResolveInfo> instantAppResolveInfoList =
- connection.getInstantAppResolveInfoList(sanitizedIntent, shaPrefix, token);
+ connection.getInstantAppResolveInfoList(sanitizedIntent,
+ requestObj.digest.getDigestPrefixSecure(), token);
if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
resolveInfo = InstantAppResolver.filterInstantAppIntent(
instantAppResolveInfoList, origIntent, requestObj.resolvedType,
- requestObj.userId, origIntent.getPackage(), digest, token);
+ requestObj.userId, origIntent.getPackage(), requestObj.digest, token);
}
} catch (ConnectionException e) {
if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -166,12 +165,6 @@
return resolveInfo;
}
- private static InstantAppDigest getInstantAppDigest(Intent origIntent) {
- return origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())
- ? new InstantAppDigest(origIntent.getData().getHost(), 5 /*maxDigests*/)
- : InstantAppDigest.UNDEFINED;
- }
-
public static void doInstantAppResolutionPhaseTwo(Context context,
InstantAppResolverConnection connection, InstantAppRequest requestObj,
ActivityInfo instantAppInstaller, Handler callbackHandler) {
@@ -182,8 +175,6 @@
}
final Intent origIntent = requestObj.origIntent;
final Intent sanitizedIntent = sanitizeIntent(origIntent);
- final InstantAppDigest digest = getInstantAppDigest(origIntent);
- final int[] shaPrefix = digest.getDigestPrefix();
final PhaseTwoCallback callback = new PhaseTwoCallback() {
@Override
@@ -194,7 +185,8 @@
final AuxiliaryResolveInfo instantAppIntentInfo =
InstantAppResolver.filterInstantAppIntent(
instantAppResolveInfoList, origIntent, null /*resolvedType*/,
- 0 /*userId*/, origIntent.getPackage(), digest, token);
+ 0 /*userId*/, origIntent.getPackage(), requestObj.digest,
+ token);
if (instantAppIntentInfo != null) {
failureIntent = instantAppIntentInfo.failureIntent;
} else {
@@ -225,8 +217,9 @@
}
};
try {
- connection.getInstantAppIntentFilterList(sanitizedIntent, shaPrefix, token, callback,
- callbackHandler, startTime);
+ connection.getInstantAppIntentFilterList(sanitizedIntent,
+ requestObj.digest.getDigestPrefixSecure(), token, callback, callbackHandler,
+ startTime);
} catch (ConnectionException e) {
@ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -377,7 +370,7 @@
failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
failureIntent.setLaunchToken(token);
ArrayList<AuxiliaryResolveInfo.AuxiliaryFilter> filters = null;
- boolean isWebIntent = origIntent.isBrowsableWebIntent();
+ boolean isWebIntent = origIntent.isWebIntent();
for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) {
if (shaPrefix.length > 0 && instantAppResolveInfo.shouldLetInstallerDecide()) {
Slog.e(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest"
@@ -448,7 +441,7 @@
instantAppInfo.getIntentFilters();
if (instantAppFilters == null || instantAppFilters.isEmpty()) {
// No filters on web intent; no matches, 2nd phase unnecessary.
- if (origIntent.isBrowsableWebIntent()) {
+ if (origIntent.isWebIntent()) {
return null;
}
// No filters; we need to start phase two
diff --git a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index a9ee411..98f421e 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -141,6 +141,7 @@
}
}
+ @GuardedBy("mLock")
private void waitForBindLocked(String token) throws TimeoutException, InterruptedException {
final long startMillis = SystemClock.uptimeMillis();
while (mBindState != STATE_IDLE) {
@@ -250,6 +251,7 @@
}
}
+ @GuardedBy("mLock")
private void handleBinderDiedLocked() {
if (mRemoteInstance != null) {
try {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 10e05cf..fc73142 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -261,12 +261,12 @@
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
@Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
- int targetSdkVersion, @Nullable String profileName)
- throws InstallerException {
+ int targetSdkVersion, @Nullable String profileName,
+ @Nullable String dexMetadataPath) throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 5.
- builder.append("5 ");
+ // The version. Right now it's 6.
+ builder.append("6 ");
builder.append("dexopt");
@@ -284,6 +284,7 @@
encodeParameter(builder, downgrade);
encodeParameter(builder, targetSdkVersion);
encodeParameter(builder, profileName);
+ encodeParameter(builder, dexMetadataPath);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cde8cb7..2c68e67 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -209,6 +210,13 @@
String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
+ String dexMetadataPath = null;
+ if (options.isDexoptInstallWithDexMetadata()) {
+ File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
+ dexMetadataPath = dexMetadataFile == null
+ ? null : dexMetadataFile.getAbsolutePath();
+ }
+
final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
|| packageUseInfo.isUsedByOtherApps(path);
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
@@ -223,7 +231,7 @@
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
- packageStats, options.isDowngrade(), profileName);
+ packageStats, options.isDowngrade(), profileName, dexMetadataPath);
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -248,7 +256,7 @@
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String classLoaderContext,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
- String profileName) {
+ String profileName, String dexMetadataPath) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -275,7 +283,7 @@
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
- profileName);
+ profileName, dexMetadataPath);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -396,7 +404,8 @@
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
- options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null);
+ options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
+ /*dexMetadataPath*/ null);
}
return DEX_OPT_PERFORMED;
@@ -511,9 +520,13 @@
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- // Profile guide compiled oat files should not be public.
+ // Profile guide compiled oat files should not be public unles they are based
+ // on profiles from dex metadata archives.
+ // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
+ // the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
+ boolean isPublic = !info.isForwardLocked() &&
+ (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// System apps are invoked with a runtime flag which exempts them from
// restrictions on hidden API usage. We dexopt with the same runtime flag
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 59f9dae..0b32d1a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -226,6 +226,7 @@
}
}
+ @GuardedBy("mSessions")
private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
final ArraySet<File> unclaimedStages = newArraySet(
@@ -283,6 +284,7 @@
}
}
+ @GuardedBy("mSessions")
private void readSessionsLocked() {
if (LOGD) Slog.v(TAG, "readSessionsLocked()");
@@ -340,6 +342,7 @@
}
}
+ @GuardedBy("mSessions")
private void addHistoricalSessionLocked(PackageInstallerSession session) {
CharArrayWriter writer = new CharArrayWriter();
IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -352,6 +355,7 @@
mHistoricalSessionsByInstaller.get(installerUid) + 1);
}
+ @GuardedBy("mSessions")
private void writeSessionsLocked() {
if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
@@ -612,6 +616,7 @@
}
}
+ @GuardedBy("mSessions")
private int allocateSessionIdLocked() {
int n = 0;
int sessionId;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a6ff4f7..9c69281 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -77,6 +77,7 @@
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
+import android.system.Int64Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
@@ -317,6 +318,7 @@
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
+ @GuardedBy("mLock")
private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
@@ -333,6 +335,7 @@
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
+ @GuardedBy("mLock")
private boolean needToAskForPermissionsLocked() {
if (mPermissionsManuallyAccepted) {
return false;
@@ -455,6 +458,7 @@
}
}
+ @GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
if (mSealed) {
@@ -462,6 +466,7 @@
}
}
+ @GuardedBy("mLock")
private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
assertPreparedAndNotDestroyedLocked(cookie);
if (mCommitted) {
@@ -469,6 +474,7 @@
}
}
+ @GuardedBy("mLock")
private void assertPreparedAndNotDestroyedLocked(String cookie) {
if (!mPrepared) {
throw new IllegalStateException(cookie + " before prepared");
@@ -483,6 +489,7 @@
* might point at an ASEC mount point, which is why we delay path resolution
* until someone actively works with the session.
*/
+ @GuardedBy("mLock")
private File resolveStageDirLocked() throws IOException {
if (mResolvedStageDir == null) {
if (stageDir != null) {
@@ -515,6 +522,7 @@
}
}
+ @GuardedBy("mLock")
private void computeProgressLocked(boolean forcePublish) {
mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
@@ -575,14 +583,24 @@
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
try {
- return openWriteInternal(name, offsetBytes, lengthBytes);
+ return doWriteInternal(name, offsetBytes, lengthBytes, null);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
- private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
- throws IOException {
+ @Override
+ public void write(String name, long offsetBytes, long lengthBytes,
+ ParcelFileDescriptor fd) {
+ try {
+ doWriteInternal(name, offsetBytes, lengthBytes, fd);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
+ ParcelFileDescriptor incomingFd) throws IOException {
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
@@ -636,7 +654,44 @@
Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
- if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ if (incomingFd != null) {
+ switch (Binder.getCallingUid()) {
+ case android.os.Process.SHELL_UID:
+ case android.os.Process.ROOT_UID:
+ break;
+ default:
+ throw new SecurityException("Reverse mode only supported from shell");
+ }
+
+ // In "reverse" mode, we're streaming data ourselves from the
+ // incoming FD, which means we never have to hand out our
+ // sensitive internal FD. We still rely on a "bridge" being
+ // inserted above to hold the session active.
+ try {
+ final Int64Ref last = new Int64Ref(0);
+ FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
+ if (params.sizeBytes > 0) {
+ final long delta = progress - last.value;
+ last.value = progress;
+ addClientProgress((float) delta / (float) params.sizeBytes);
+ }
+ }, null, lengthBytes);
+ } finally {
+ IoUtils.closeQuietly(targetFd);
+ IoUtils.closeQuietly(incomingFd);
+
+ // We're done here, so remove the "bridge" that was holding
+ // the session active.
+ synchronized (mLock) {
+ if (PackageInstaller.ENABLE_REVOCABLE_FD) {
+ mFds.remove(fd);
+ } else {
+ mBridges.remove(bridge);
+ }
+ }
+ }
+ return null;
+ } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd.init(mContext, targetFd);
return fd.getRevocableFileDescriptor();
} else {
@@ -680,6 +735,7 @@
* Check if the caller is the owner of this session. Otherwise throw a
* {@link SecurityException}.
*/
+ @GuardedBy("mLock")
private void assertCallerIsOwnerOrRootLocked() {
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
@@ -690,6 +746,7 @@
/**
* If anybody is reading or writing data of the session, throw an {@link SecurityException}.
*/
+ @GuardedBy("mLock")
private void assertNoWriteFileTransfersOpenLocked() {
// Verify that all writers are hands-off
for (RevocableFileDescriptor fd : mFds) {
@@ -772,6 +829,7 @@
* @throws PackageManagerException if the session was sealed but something went wrong. If the
* session was sealed this is the only possible exception.
*/
+ @GuardedBy("mLock")
private void sealAndValidateLocked() throws PackageManagerException, IOException {
assertNoWriteFileTransfersOpenLocked();
assertPreparedAndNotDestroyedLocked("sealing of session");
@@ -853,6 +911,7 @@
mCallback.onSessionSealedBlocking(this);
}
+ @GuardedBy("mLock")
private void commitLocked()
throws PackageManagerException {
if (mDestroyed) {
@@ -968,6 +1027,7 @@
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
+ @GuardedBy("mLock")
private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
throws PackageManagerException {
mPackageName = null;
@@ -1180,6 +1240,7 @@
}
}
+ @GuardedBy("mLock")
private void assertApkConsistentLocked(String tag, ApkLite apk)
throws PackageManagerException {
if (!mPackageName.equals(apk.packageName)) {
@@ -1463,6 +1524,7 @@
}
}
+ @GuardedBy("mLock")
private void dumpLocked(IndentingPrintWriter pw) {
pw.println("Session " + sessionId + ":");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89fbd17..384b074 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -797,6 +797,7 @@
return overlayPackages;
}
+ @GuardedBy("mInstallLock")
final String[] getStaticOverlayPathsLocked(Collection<PackageParser.Package> allPackages,
String targetPackageName, String targetPath) {
if ("android".equals(targetPackageName)) {
@@ -2746,12 +2747,6 @@
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) {
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Possibly deleted app: " + ps.dumpState_temp()
- + "; path: " + (disabledPs.codePath == null ? "<<NULL>>":disabledPs.codePath)
- + "; pkg: " + (disabledPs.pkg==null?"<<NULL>>":disabledPs.pkg.toString()));
-}
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
@@ -2803,10 +2798,6 @@
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "remove update; name: " + deletedAppName + ", exists? " + (deletedPkg != null));
-}
final String msg;
if (deletedPkg == null) {
// should have found an update, but, we didn't; remove everything
@@ -3840,10 +3831,6 @@
if (ps == null) {
return null;
}
- PackageParser.Package p = ps.pkg;
- if (p == null) {
- return null;
- }
final int callingUid = Binder.getCallingUid();
// Filter out ephemeral app metadata:
// * The system/shell/root can see metadata for any app
@@ -3855,32 +3842,58 @@
return null;
}
- final PermissionsState permissionsState = ps.getPermissionsState();
-
- // Compute GIDs only if requested
- final int[] gids = (flags & PackageManager.GET_GIDS) == 0
- ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
- // Compute granted permissions only if package has requested permissions
- final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
- ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
- final PackageUserState state = ps.readUserState(userId);
-
if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& ps.isSystem()) {
flags |= MATCH_ANY_USER;
}
- PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
+ final PackageUserState state = ps.readUserState(userId);
+ PackageParser.Package p = ps.pkg;
+ if (p != null) {
+ final PermissionsState permissionsState = ps.getPermissionsState();
- if (packageInfo == null) {
+ // Compute GIDs only if requested
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0
+ ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
+ // Compute granted permissions only if package has requested permissions
+ final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
+ ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
+
+ PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
+
+ if (packageInfo == null) {
+ return null;
+ }
+
+ packageInfo.packageName = packageInfo.applicationInfo.packageName =
+ resolveExternalPackageNameLPr(p);
+
+ return packageInfo;
+ } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = ps.name;
+ pi.setLongVersionCode(ps.versionCode);
+ pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
+ pi.firstInstallTime = ps.firstInstallTime;
+ pi.lastUpdateTime = ps.lastUpdateTime;
+
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = ps.name;
+ ai.uid = UserHandle.getUid(userId, ps.appId);
+ ai.primaryCpuAbi = ps.primaryCpuAbiString;
+ ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
+ ai.versionCode = ps.versionCode;
+ ai.flags = ps.pkgFlags;
+ ai.privateFlags = ps.pkgPrivateFlags;
+ pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId);
+
+ if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ + ps.name + "]. Provides a minimum info.");
+ return pi;
+ } else {
return null;
}
-
- packageInfo.packageName = packageInfo.applicationInfo.packageName =
- resolveExternalPackageNameLPr(p);
-
- return packageInfo;
}
@Override
@@ -5960,7 +5973,7 @@
if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
- if (!intent.isBrowsableWebIntent()) {
+ if (!intent.isWebIntent()) {
// for non web intents, we should not resolve externally if an app already exists to
// handle it or if the caller didn't explicitly request it.
if ((resolvedActivities != null && resolvedActivities.size() != 0)
@@ -6671,7 +6684,7 @@
ai.packageName, ai.versionCode, null /* splitName */);
}
}
- if (intent.isBrowsableWebIntent() && auxiliaryResponse == null) {
+ if (intent.isWebIntent() && auxiliaryResponse == null) {
return result;
}
final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
@@ -8512,8 +8525,6 @@
return false;
}
- // Temporary to catch potential issues with refactoring
- private static boolean REFACTOR_DEBUG = true;
/**
* Adds a new package to the internal data structures during platform initialization.
* <p>After adding, the package is known to the system and available for querying.
@@ -8554,10 +8565,6 @@
synchronized (mPackages) {
renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
final String realPkgName = getRealPackageName(pkg, renamedPkgName);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Add pkg: " + pkg.packageName + (realPkgName==null?"":", realName: " + realPkgName));
-}
if (realPkgName != null) {
ensurePackageRenamed(pkg, renamedPkgName);
}
@@ -8572,12 +8579,6 @@
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "SSP? " + scanSystemPartition
- + ", exists? " + pkgAlreadyExists + (pkgAlreadyExists?" "+pkgSetting.toString():"")
- + ", upgraded? " + isSystemPkgUpdated + (isSystemPkgUpdated?" "+disabledPkgSetting.toString():""));
-}
final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
? mSettings.getSharedUserLPw(pkg.mSharedUserId,
@@ -8589,12 +8590,6 @@
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Shared UserID " + pkg.mSharedUserId
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
-}
}
if (scanSystemPartition) {
@@ -8603,10 +8598,6 @@
// version on /data, cycle through all of its children packages and
// remove children that are no longer defined.
if (isSystemPkgUpdated) {
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Disable child packages");
-}
final int scannedChildCount = (pkg.childPackages != null)
? pkg.childPackages.size() : 0;
final int disabledChildCount = disabledPkgSetting.childPackageNames != null
@@ -8618,19 +8609,11 @@
for (int j = 0; j < scannedChildCount; j++) {
PackageParser.Package childPkg = pkg.childPackages.get(j);
if (childPkg.packageName.equals(disabledChildPackageName)) {
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Ignore " + disabledChildPackageName);
-}
disabledPackageAvailable = true;
break;
}
}
if (!disabledPackageAvailable) {
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Disable " + disabledChildPackageName);
-}
mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
}
}
@@ -8639,44 +8622,17 @@
disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Scan disabled system package");
-Slog.e("TODD",
- "Pre: " + request.pkgSetting.dumpState_temp());
-}
-final ScanResult result =
scanPackageOnlyLI(request, mFactoryTest, -1L);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Post: " + (result.success?result.pkgSetting.dumpState_temp():"FAILED scan"));
-}
}
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "paths changed? " + newPkgChangedPaths
- + "; old: " + pkg.codePath
- + ", new: " + (pkgSetting==null?"<<NULL>>":pkgSetting.codePathString));
-}
final boolean newPkgVersionGreater =
pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode;
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "version greater? " + newPkgVersionGreater
- + "; old: " + pkg.getLongVersionCode()
- + ", new: " + (pkgSetting==null?"<<NULL>>":pkgSetting.versionCode));
-}
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
-if (REFACTOR_DEBUG) {
- Slog.e("TODD",
- "system better? " + isSystemPkgBetter);
-}
if (isSystemPkgBetter) {
// The version of the application on /system is greater than the version on
// /data. Switch back to the application on /system.
@@ -8692,13 +8648,6 @@
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "System package changed;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
- + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
-}
final InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
@@ -8710,10 +8659,6 @@
}
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "THROW exception; system pkg version not good enough");
-}
// The version of the application on the /system partition is less than or
// equal to the version on the /data partition. Throw an exception and use
// the application already installed on the /data partition.
@@ -8744,11 +8689,6 @@
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "System package signature mismatch;"
- + " name: " + pkgSetting.name);
-}
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
@@ -8763,13 +8703,6 @@
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "System package enabled;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
- + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
-}
InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
@@ -8786,35 +8719,13 @@
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ "; new: " + pkg.codePath + " @ " + pkg.codePath);
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "System package disabled;"
- + " name: " + pkgSetting.name
- + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
- + "; new: " + pkg.codePath + " @ " + pkg.codePath);
-}
}
}
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Scan package");
-Slog.e("TODD",
- "Pre: " + (pkgSetting==null?"<<NONE>>":pkgSetting.dumpState_temp()));
-}
final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
-if (REFACTOR_DEBUG) {
-pkgSetting = mSettings.getPackageLPr(pkg.packageName);
-Slog.e("TODD",
- "Post: " + (pkgSetting==null?"<<NONE>>":pkgSetting.dumpState_temp()));
-}
if (shouldHideSystemApp) {
-if (REFACTOR_DEBUG) {
-Slog.e("TODD",
- "Disable package: " + pkg.packageName);
-}
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
@@ -9105,6 +9016,7 @@
}
}
+ @GuardedBy("mPackages")
private void notifyPackageUseLocked(String packageName, int reason) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
@@ -10715,8 +10627,6 @@
~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
pkg.applicationInfo.privateFlags &=
~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
- // clear protected broadcasts
- pkg.protectedBroadcasts = null;
// cap permission priorities
if (pkg.permissionGroups != null && pkg.permissionGroups.size() > 0) {
for (int i = pkg.permissionGroups.size() - 1; i >= 0; --i) {
@@ -10725,6 +10635,8 @@
}
}
if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ // clear protected broadcasts
+ pkg.protectedBroadcasts = null;
// ignore export request for single user receivers
if (pkg.receivers != null) {
for (int i = pkg.receivers.size() - 1; i >= 0; --i) {
@@ -14044,6 +13956,7 @@
}
}
+ @GuardedBy("mPackages")
private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
if (isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
@@ -16700,6 +16613,12 @@
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
+ } else {
+ for (int currentUserId : sUserManager.getUserIds()) {
+ ps.setInstalled(true, currentUserId);
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
+ installerPackageName);
+ }
}
// When replacing an existing package, preserve the original install reason for all
@@ -17309,7 +17228,8 @@
// Also, don't fail application installs if the dexopt step fails.
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
@@ -20913,9 +20833,6 @@
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED) {
- return Zygote.MOUNT_EXTERNAL_DEFAULT;
- }
if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_DEFAULT;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index c02331d..5060c4d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -693,7 +693,7 @@
InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
) {
- Streams.copy(fileIn, fileOut);
+ FileUtils.copy(fileIn, fileOut);
Os.chmod(dstFile.getAbsolutePath(), 0644);
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 686c4a5..758c9d5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -16,6 +16,12 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -32,8 +38,10 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
@@ -41,9 +49,6 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionParams;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
@@ -73,7 +78,6 @@
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.SizedInputStream;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
@@ -81,9 +85,8 @@
import libcore.io.IoUtils;
+import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
@@ -95,12 +98,6 @@
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
@@ -2213,7 +2210,7 @@
final PrintWriter pw = getOutPrintWriter();
final ParcelFileDescriptor fd;
if (STDIN_PATH.equals(inPath)) {
- fd = null;
+ fd = new ParcelFileDescriptor(getInFileDescriptor());
} else if (inPath != null) {
fd = openFileForSystem(inPath, "r");
if (fd == null) {
@@ -2225,53 +2222,27 @@
return -1;
}
} else {
- fd = null;
+ fd = new ParcelFileDescriptor(getInFileDescriptor());
}
if (sizeBytes <= 0) {
getErrPrintWriter().println("Error: must specify a APK size");
return 1;
}
- final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
-
PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
-
- if (fd != null) {
- in = new ParcelFileDescriptor.AutoCloseInputStream(fd);
- } else {
- in = new SizedInputStream(getRawInputStream(), sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[1024 * 1024];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
+ session.write(splitName, 0, sizeBytes, fd);
if (logSuccess) {
- pw.println("Success: streamed " + total + " bytes");
+ pw.println("Success: streamed " + sizeBytes + " bytes");
}
return 0;
} catch (IOException e) {
getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
return 1;
} finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
IoUtils.closeQuietly(session);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 3e2bd4a..ea05b74 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -97,35 +97,6 @@
+ " " + name + "/" + appId + "}";
}
- // Temporary to catch potential issues with refactoring
- public String dumpState_temp() {
- String flags = "";
- flags += ((pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
- flags += ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
- if ("".equals(flags)) {
- flags = "-";
- }
- String privFlags = "";
- privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
- privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
- privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
- if ("".equals(privFlags)) {
- privFlags = "-";
- }
- return "PackageSetting{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + name + (realName == null ? "" : "("+realName+")") + "/" + appId + (sharedUser==null?"":" u:" + sharedUser.name+"("+sharedUserId+")")
- + ", ver:" + versionCode
- + ", path: " + codePath
- + ", pABI: " + primaryCpuAbiString
- + ", sABI: " + secondaryCpuAbiString
- + ", oABI: " + cpuAbiOverrideString
- + ", flags: " + flags
- + ", privFlags: " + privFlags
- + ", pkg: " + (pkg == null ? "<<NULL>>" : pkg.dumpState_temp())
- + "}";
- }
-
public void copyFrom(PackageSetting orig) {
super.copyFrom(orig);
doCopy(orig);
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 95f490e..471729e 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -318,7 +318,7 @@
mSigningDetails.signatures[i].hashCode()));
}
}
- buf.append("]}");
+ buf.append("]");
buf.append(", past signatures:[");
if (mSigningDetails.pastSigningCertificates != null) {
for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
@@ -329,6 +329,7 @@
buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i]));
}
}
+ buf.append("]}");
return buf.toString();
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ebf6672..e4c74ed 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -703,7 +703,7 @@
*/
public boolean rescanPackageIfNeeded(boolean isNewApp, boolean forceRescan) {
final ShortcutService s = mShortcutUser.mService;
- final long start = s.injectElapsedRealtime();
+ final long start = s.getStatStartTime();
final PackageInfo pi;
try {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d2bc6d2..034fd23 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -99,6 +99,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.StatLogger;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -367,7 +368,7 @@
int COUNT = GET_DEFAULT_LAUNCHER + 1;
}
- private static final String[] STAT_LABELS = {
+ private final StatLogger mStatLogger = new StatLogger(new String[] {
"getHomeActivities()",
"Launcher permission check",
"getPackageInfo()",
@@ -385,15 +386,7 @@
"packageUpdateCheck",
"asyncPreloadUserDelay",
"getDefaultLauncher()"
- };
-
- final Object mStatLock = new Object();
-
- @GuardedBy("mStatLock")
- private final int[] mCountStats = new int[Stats.COUNT];
-
- @GuardedBy("mStatLock")
- private final long[] mDurationStats = new long[Stats.COUNT];
+ });
private static final int PROCESS_STATE_FOREGROUND_THRESHOLD =
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
@@ -480,11 +473,12 @@
| ActivityManager.UID_OBSERVER_GONE);
}
+ long getStatStartTime() {
+ return mStatLogger.getTime();
+ }
+
void logDurationStat(int statId, long start) {
- synchronized (mStatLock) {
- mCountStats[statId]++;
- mDurationStats[statId] += (injectElapsedRealtime() - start);
- }
+ mStatLogger.logDurationStat(statId, start);
}
public String injectGetLocaleTagsForUser(@UserIdInt int userId) {
@@ -536,6 +530,7 @@
return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
}
+ @GuardedBy("mLock")
boolean isUidForegroundLocked(int uid) {
if (uid == Process.SYSTEM_UID) {
// IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
@@ -551,6 +546,7 @@
return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid));
}
+ @GuardedBy("mLock")
long getUidLastForegroundElapsedTimeLocked(int uid) {
return mUidLastForegroundElapsedTime.get(uid);
}
@@ -621,7 +617,7 @@
// late since the launcher would already have started.
// So we just create a new thread. This code runs rarely, so we don't use a thread pool
// or anything.
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
injectRunOnNewThread(() -> {
synchronized (mLock) {
logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
@@ -644,6 +640,7 @@
}
}
+ @GuardedBy("mLock")
private void unloadUserLocked(int userId) {
if (DEBUG) {
Slog.d(TAG, "unloadUserLocked: user=" + userId);
@@ -870,6 +867,7 @@
writeAttr(out, name, intent.toUri(/* flags =*/ 0));
}
+ @GuardedBy("mLock")
@VisibleForTesting
void saveBaseStateLocked() {
final AtomicFile file = getBaseStateFile();
@@ -902,6 +900,7 @@
}
}
+ @GuardedBy("mLock")
private void loadBaseStateLocked() {
mRawLastResetTime = 0;
@@ -954,6 +953,7 @@
return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
}
+ @GuardedBy("mLock")
private void saveUserLocked(@UserIdInt int userId) {
final File path = getUserFile(userId);
if (DEBUG) {
@@ -980,6 +980,7 @@
}
}
+ @GuardedBy("mLock")
private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
boolean forBackup) throws IOException, XmlPullParserException {
@@ -1113,12 +1114,14 @@
}
/** Return the last reset time. */
+ @GuardedBy("mLock")
long getLastResetTimeLocked() {
updateTimesLocked();
return mRawLastResetTime;
}
/** Return the next reset time. */
+ @GuardedBy("mLock")
long getNextResetTimeLocked() {
updateTimesLocked();
return mRawLastResetTime + mResetInterval;
@@ -1131,6 +1134,7 @@
/**
* Update the last reset time.
*/
+ @GuardedBy("mLock")
private void updateTimesLocked() {
final long now = injectCurrentTimeMillis();
@@ -1226,6 +1230,7 @@
return ret;
}
+ @GuardedBy("mLock")
void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
for (int i = mUsers.size() - 1; i >= 0; i--) {
c.accept(mUsers.valueAt(i));
@@ -1285,11 +1290,12 @@
* {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
* saves are going on.
*/
+ @GuardedBy("mLock")
private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -1485,7 +1491,7 @@
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
si.getPackage(), si.getUserId());
if (publisherRes != null) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
si.lookupAndFillInResourceNames(publisherRes);
} finally {
@@ -2114,6 +2120,7 @@
}
}
+ @GuardedBy("mLock")
private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
@UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
@@ -2264,7 +2271,7 @@
if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) {
return true;
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return hasShortcutHostPermissionInner(callingPackage, userId);
} finally {
@@ -2327,7 +2334,7 @@
@Nullable
ComponentName getDefaultLauncher(@UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
synchronized (mLock) {
@@ -2338,7 +2345,7 @@
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
// Default launcher from package manager.
- final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
+ final long startGetHomeActivitiesAsUser = getStatStartTime();
final ComponentName defaultLauncher = mPackageManagerInternal
.getHomeActivitiesAsUser(allHomeCandidates, userId);
logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
@@ -2424,6 +2431,7 @@
*
* This is called when an app is uninstalled, or an app gets "clear data"ed.
*/
+ @GuardedBy("mLock")
@VisibleForTesting
void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
boolean appStillExists) {
@@ -2514,6 +2522,7 @@
return setReturnedByServer(ret);
}
+ @GuardedBy("ShortcutService.this.mLock")
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
@@ -2585,6 +2594,7 @@
}
}
+ @GuardedBy("ShortcutService.this.mLock")
private ShortcutInfo getShortcutInfoLocked(
int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId,
@@ -2910,7 +2920,7 @@
return;
}
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
@@ -2946,6 +2956,7 @@
verifyStates();
}
+ @GuardedBy("mLock")
private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -3087,7 +3098,7 @@
@VisibleForTesting
PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
boolean getSignatures) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getPackageInfo(
@@ -3122,7 +3133,7 @@
@VisibleForTesting
ApplicationInfo injectApplicationInfoWithUninstalled(
String packageName, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
@@ -3153,7 +3164,7 @@
@VisibleForTesting
ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(
ComponentName activity, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getActivityInfo(activity,
@@ -3175,7 +3186,7 @@
@NonNull
@VisibleForTesting
final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
@@ -3280,7 +3291,7 @@
@Nullable
Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
return mContext.getPackageManager().getResourcesForApplicationAsUser(
@@ -3348,7 +3359,7 @@
*/
@Nullable
ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
final List<ResolveInfo> resolved =
queryActivities(getMainActivityIntent(), packageName, null, userId);
@@ -3362,7 +3373,7 @@
* Return whether an activity is enabled, exported and main.
*/
boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
if (activity == null) {
wtf("null activity detected");
@@ -3397,7 +3408,7 @@
*/
@NonNull
List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return queryActivities(getMainActivityIntent(), packageName, null, userId);
} finally {
@@ -3411,7 +3422,7 @@
@VisibleForTesting
boolean injectIsActivityEnabledAndExported(
@NonNull ComponentName activity, @UserIdInt int userId) {
- final long start = injectElapsedRealtime();
+ final long start = getStatStartTime();
try {
return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
.size() > 0;
@@ -3831,12 +3842,7 @@
pw.println(mMaxShortcuts);
pw.println();
- pw.println(" Stats:");
- synchronized (mStatLock) {
- for (int i = 0; i < Stats.COUNT; i++) {
- dumpStatLS(pw, " ", i);
- }
- }
+ mStatLogger.dump(pw, " ");
pw.println();
pw.print(" #Failures: ");
@@ -3902,15 +3908,6 @@
pw.print(formatTime(injectCurrentTimeMillis()));
}
- private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
- pw.print(prefix);
- final int count = mCountStats[statId];
- final long dur = mDurationStats[statId];
- pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
- STAT_LABELS[statId], count, dur,
- (count == 0 ? 0 : ((double) dur) / count)));
- }
-
/**
* Dumpsys for checkin.
*
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 92fd904..a0577b1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -88,6 +88,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -387,7 +388,9 @@
}
final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
- setQuietModeEnabled(userHandle, false, target);
+ // Call setQuietModeEnabled on bg thread to avoid ANR
+ BackgroundThread.getHandler()
+ .post(() -> setQuietModeEnabled(userHandle, false, target));
}
};
@@ -1544,6 +1547,7 @@
return result;
}
+ @GuardedBy("mRestrictionsLock")
private EnforcingUser getEnforcingUserLocked(@UserIdInt int userId) {
int source = mDeviceOwnerUserId == userId ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER
: UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
@@ -2893,6 +2897,7 @@
}
}
+ @GuardedBy("mUsersLock")
@VisibleForTesting
void addRemovingUserIdLocked(int userId) {
// We remember deleted user IDs to prevent them from being
@@ -3402,6 +3407,7 @@
return nextId;
}
+ @GuardedBy("mUsersLock")
private int scanNextAvailableIdLocked() {
for (int i = MIN_USER_ID; i < MAX_USER_ID; i++) {
if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index a42fcbd..23185d7 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -117,12 +117,13 @@
UserManager.DISALLOW_AUTOFILL,
UserManager.DISALLOW_USER_SWITCH,
UserManager.DISALLOW_UNIFIED_PASSWORD,
- UserManager.DISALLOW_CONFIG_LOCATION_MODE,
+ UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_CONFIG_BRIGHTNESS,
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
UserManager.DISALLOW_AMBIENT_DISPLAY,
- UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT
+ UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
+ UserManager.DISALLOW_PRINTING
});
/**
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index 0966770..d4f95cb 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -59,6 +59,10 @@
// When set, indicates that dexopt is invoked from the background service.
public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+ // When set, indicates that dexopt is invoked from the install time flow and
+ // should get the dex metdata file if present.
+ public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+
// The name of package to optimize.
private final String mPackageName;
@@ -90,7 +94,8 @@
DEXOPT_ONLY_SHARED_DEX |
DEXOPT_DOWNGRADE |
DEXOPT_AS_SHARED_LIBRARY |
- DEXOPT_IDLE_BACKGROUND_JOB;
+ DEXOPT_IDLE_BACKGROUND_JOB |
+ DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -141,6 +146,10 @@
return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
}
+ public boolean isDexoptInstallWithDexMetadata() {
+ return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 08f8bbd..3f8a1e8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1210,6 +1210,7 @@
return false;
}
+ @GuardedBy("mLock")
private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
if (pkg.parentPackage == null) {
@@ -1499,6 +1500,7 @@
}
}
+ @GuardedBy("mLock")
private int[] revokeUnusedSharedUserPermissionsLocked(
SharedUserSetting suSetting, int[] allUserIds) {
// Collect all used permissions in the UID
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index f6c4990..b3f2833 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -201,34 +201,42 @@
}
}
+ @GuardedBy("mLock")
@Nullable BasePermission getPermissionLocked(@NonNull String permName) {
return mPermissions.get(permName);
}
+ @GuardedBy("mLock")
@Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
return mPermissionTrees.get(permName);
}
+ @GuardedBy("mLock")
void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
mPermissions.put(permName, permission);
}
+ @GuardedBy("mLock")
void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
mPermissionTrees.put(permName, permission);
}
+ @GuardedBy("mLock")
void removePermissionLocked(@NonNull String permName) {
mPermissions.remove(permName);
}
+ @GuardedBy("mLock")
void removePermissionTreeLocked(@NonNull String permName) {
mPermissionTrees.remove(permName);
}
+ @GuardedBy("mLock")
@NonNull Collection<BasePermission> getAllPermissionsLocked() {
return mPermissions.values();
}
+ @GuardedBy("mLock")
@NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
return mPermissionTrees.values();
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 847c90a..08dc97e 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -306,6 +306,7 @@
}
}
+ @GuardedBy("mLock")
@VisibleForTesting
void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
mSettings = setting;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index a536270..3072f21 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -85,6 +85,7 @@
private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 4;
private static final int MSG_PROFILE_TIMED_OUT = 5;
+ private static final int MSG_WIRED_CHARGING_STARTED = 6;
private final Object mLock = new Object();
@@ -571,6 +572,20 @@
mHandler.sendMessage(msg);
}
+ /**
+ * Called when wired charging has started so as to provide user feedback
+ */
+ public void onWiredChargingStarted() {
+ if (DEBUG) {
+ Slog.d(TAG, "onWiredChargingStarted");
+ }
+
+ mSuspendBlocker.acquire();
+ Message msg = mHandler.obtainMessage(MSG_WIRED_CHARGING_STARTED);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+
private void updatePendingBroadcastLocked() {
if (!mBroadcastInProgress
&& mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
@@ -703,11 +718,14 @@
}
};
- private void playWirelessChargingStartedSound() {
+ /**
+ * Plays the wireless charging sound for both wireless and non-wireless charging
+ */
+ private void playChargingStartedSound() {
final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
+ Settings.Global.CHARGING_STARTED_SOUND);
if (enabled && soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
@@ -721,11 +739,16 @@
}
private void showWirelessChargingStarted(int batteryLevel) {
- playWirelessChargingStartedSound();
+ playChargingStartedSound();
mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
mSuspendBlocker.release();
}
+ private void showWiredChargingStarted() {
+ playChargingStartedSound();
+ mSuspendBlocker.release();
+ }
+
private void lockProfile(@UserIdInt int userId) {
mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
}
@@ -753,6 +776,8 @@
case MSG_PROFILE_TIMED_OUT:
lockProfile(msg.arg1);
break;
+ case MSG_WIRED_CHARGING_STARTED:
+ showWiredChargingStarted();
}
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index db83158..38dc820 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -120,9 +120,6 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEW = DEBUG && true;
- // if DEBUG_WIRELESS=true, plays wireless charging animation w/ sound on every plug + unplug
- private static final boolean DEBUG_WIRELESS = false;
-
// Message: Sent when a user activity timeout occurs to update the power state.
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: Sent when the device enters or exits a dreaming or dozing state.
@@ -1743,10 +1740,15 @@
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
- // Tell the notifier whether wireless charging has started so that
- // it can provide feedback to the user.
- if (dockedOnWirelessCharger || DEBUG_WIRELESS) {
- mNotifier.onWirelessChargingStarted(mBatteryLevel);
+ // only play charging sounds if boot is completed so charging sounds don't play
+ // with potential notification sounds
+ if (mBootCompleted) {
+ if (mIsPowered && !BatteryManager.isPlugWired(oldPlugType)
+ && BatteryManager.isPlugWired(mPlugType)) {
+ mNotifier.onWiredChargingStarted();
+ } else if (dockedOnWirelessCharger) {
+ mNotifier.onWirelessChargingStarted(mBatteryLevel);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 9466350..37df94f 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -32,6 +32,8 @@
/**
* This class keeps track of battery drain rate.
*
+ * TODO: The use of the terms "percent" and "level" in this class is not standard. Fix it.
+ *
* Test:
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
*/
@@ -96,8 +98,12 @@
public int startBatteryLevel;
public int endBatteryLevel;
+ public int startBatteryPercent;
+ public int endBatteryPercent;
+
public long totalTimeMillis;
public int totalBatteryDrain;
+ public int totalBatteryDrainPercent;
public long totalMinutes() {
return totalTimeMillis / 60_000;
@@ -110,14 +116,26 @@
return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000));
}
+ public double drainPercentPerHour() {
+ if (totalTimeMillis == 0) {
+ return 0;
+ }
+ return (double) totalBatteryDrainPercent / (totalTimeMillis / (60.0 * 60 * 1000));
+ }
+
@VisibleForTesting
String toStringForTest() {
return "{" + totalMinutes() + "m," + totalBatteryDrain + ","
- + String.format("%.2f", drainPerHour()) + "}";
+ + String.format("%.2f", drainPerHour()) + "uA/H,"
+ + String.format("%.2f", drainPercentPerHour()) + "%"
+ + "}";
}
}
@VisibleForTesting
+ static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
+
+ @VisibleForTesting
static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
@VisibleForTesting
@@ -166,6 +184,9 @@
private BatteryManagerInternal getBatteryManagerInternal() {
if (mBatteryManagerInternal == null) {
mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ if (mBatteryManagerInternal == null) {
+ Slog.wtf(TAG, "BatteryManagerInternal not initialized");
+ }
}
return mBatteryManagerInternal;
}
@@ -229,12 +250,20 @@
int injectBatteryLevel() {
final BatteryManagerInternal bmi = getBatteryManagerInternal();
if (bmi == null) {
- Slog.wtf(TAG, "BatteryManagerInternal not initialized");
return 0;
}
return bmi.getBatteryChargeCounter();
}
+ @VisibleForTesting
+ int injectBatteryPercent() {
+ final BatteryManagerInternal bmi = getBatteryManagerInternal();
+ if (bmi == null) {
+ return 0;
+ }
+ return bmi.getBatteryLevel();
+ }
+
/**
* Called from the outside whenever any of the states changes, when the device is not plugged
* in.
@@ -256,39 +285,47 @@
}
}
+ @GuardedBy("mLock")
private void transitionStateLocked(int newState) {
if (mCurrentState == newState) {
return;
}
final long now = injectCurrentTime();
final int batteryLevel = injectBatteryLevel();
+ final int batteryPercent = injectBatteryPercent();
- endLastStateLocked(now, batteryLevel);
- startNewStateLocked(newState, now, batteryLevel);
- mMetricsLoggerHelper.transitionState(newState, now, batteryLevel);
+ endLastStateLocked(now, batteryLevel, batteryPercent);
+ startNewStateLocked(newState, now, batteryLevel, batteryPercent);
+ mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent);
}
- private void endLastStateLocked(long now, int batteryLevel) {
+ @GuardedBy("mLock")
+ private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) {
if (mCurrentState < 0) {
return;
}
final Stat stat = getStat(mCurrentState);
stat.endBatteryLevel = batteryLevel;
+ stat.endBatteryPercent = batteryPercent;
stat.endTime = now;
final long deltaTime = stat.endTime - stat.startTime;
final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
+ final int deltaPercent = stat.startBatteryPercent - stat.endBatteryPercent;
stat.totalTimeMillis += deltaTime;
stat.totalBatteryDrain += deltaDrain;
+ stat.totalBatteryDrainPercent += deltaPercent;
if (DEBUG) {
Slog.d(TAG, "State summary: " + stateToString(mCurrentState)
+ ": " + (deltaTime / 1_000) + "s "
+ "Start level: " + stat.startBatteryLevel + "uA "
+ "End level: " + stat.endBatteryLevel + "uA "
- + deltaDrain + "uA");
+ + "Start percent: " + stat.startBatteryPercent + "% "
+ + "End percent: " + stat.endBatteryPercent + "% "
+ + "Drain " + deltaDrain + "uA");
}
EventLogTags.writeBatterySavingStats(
BatterySaverState.fromIndex(mCurrentState),
@@ -296,12 +333,15 @@
DozeState.fromIndex(mCurrentState),
deltaTime,
deltaDrain,
+ deltaPercent,
stat.totalTimeMillis,
- stat.totalBatteryDrain);
+ stat.totalBatteryDrain,
+ stat.totalBatteryDrainPercent);
}
- private void startNewStateLocked(int newState, long now, int batteryLevel) {
+ @GuardedBy("mLock")
+ private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) {
if (DEBUG) {
Slog.d(TAG, "New state: " + stateToString(newState));
}
@@ -313,6 +353,7 @@
final Stat stat = getStat(mCurrentState);
stat.startBatteryLevel = batteryLevel;
+ stat.startBatteryPercent = batteryPercent;
stat.startTime = now;
stat.endTime = 0;
}
@@ -325,7 +366,7 @@
indent = indent + " ";
pw.print(indent);
- pw.println("Battery Saver: Off On");
+ pw.println("Battery Saver: w/Off w/On");
dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
@@ -357,12 +398,14 @@
final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
- pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h",
+ pw.println(String.format("%6dm %6dmA (%3d%%) %8.1fmA/h %6dm %6dmA (%3d%%) %8.1fmA/h",
offStat.totalMinutes(),
offStat.totalBatteryDrain / 1000,
+ offStat.totalBatteryDrainPercent,
offStat.drainPerHour() / 1000.0,
onStat.totalMinutes(),
onStat.totalBatteryDrain / 1000,
+ onStat.totalBatteryDrainPercent,
onStat.drainPerHour() / 1000.0));
}
@@ -371,12 +414,13 @@
private int mLastState = STATE_NOT_INITIALIZED;
private long mStartTime;
private int mStartBatteryLevel;
+ private int mStartPercent;
private static final int STATE_CHANGE_DETECT_MASK =
(BatterySaverState.MASK << BatterySaverState.SHIFT) |
(InteractiveState.MASK << InteractiveState.SHIFT);
- public void transitionState(int newState, long now, int batteryLevel) {
+ public void transitionState(int newState, long now, int batteryLevel, int batteryPercent) {
final boolean stateChanging =
((mLastState >= 0) ^ (newState >= 0)) ||
(((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
@@ -384,11 +428,13 @@
if (mLastState >= 0) {
final long deltaTime = now - mStartTime;
final int deltaBattery = mStartBatteryLevel - batteryLevel;
+ final int deltaPercent = mStartPercent - batteryPercent;
- report(mLastState, deltaTime, deltaBattery);
+ report(mLastState, deltaTime, deltaBattery, deltaPercent);
}
mStartTime = now;
mStartBatteryLevel = batteryLevel;
+ mStartPercent = batteryPercent;
}
mLastState = newState;
}
@@ -405,9 +451,10 @@
}
}
- void report(int state, long deltaTimeMs, int deltaBatteryUa) {
+ void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
final String suffix = getCounterSuffix(state);
mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
+ mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
index e0ab9e9..c08b610 100644
--- a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
+++ b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
@@ -306,6 +306,7 @@
}
}
+ @GuardedBy("mLock")
private void saveDefaultValuesLocked() {
final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
@@ -334,6 +335,7 @@
}
}
+ @GuardedBy("mLock")
@VisibleForTesting
boolean loadDefaultValuesLocked() {
final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
index 591e809..6f5afa2 100644
--- a/services/core/java/com/android/server/slice/SliceFullAccessList.java
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -16,9 +16,9 @@
import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
-import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.XmlUtils;
@@ -63,7 +63,16 @@
pkgs.add(pkg);
}
- public void writeXml(XmlSerializer out) throws IOException {
+ public void removeGrant(String pkg, int userId) {
+ ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mFullAccessPkgs.put(userId, pkgs);
+ }
+ pkgs.remove(pkg);
+ }
+
+ public void writeXml(XmlSerializer out, int user) throws IOException {
out.startTag(null, TAG_LIST);
out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
@@ -71,6 +80,9 @@
for (int i = 0 ; i < N; i++) {
final int userId = mFullAccessPkgs.keyAt(i);
final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i);
+ if (user != UserHandle.USER_ALL && user != userId) {
+ continue;
+ }
out.startTag(null, TAG_USER);
out.attribute(null, ATT_USER_ID, Integer.toString(userId));
if (pkgs != null) {
@@ -79,7 +91,6 @@
out.startTag(null, TAG_PKG);
out.text(pkgs.valueAt(j));
out.endTag(null, TAG_PKG);
-
}
}
out.endTag(null, TAG_USER);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5db0fc0..a1def44 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -21,6 +21,7 @@
import static android.content.ContentProvider.maybeAddUserId;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
import android.Manifest.permission;
import android.app.ActivityManager;
@@ -31,9 +32,11 @@
import android.app.slice.ISliceManager;
import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
@@ -66,8 +69,9 @@
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -91,7 +95,9 @@
private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
private final ContentObserver mObserver;
+ @GuardedBy("mSliceAccessFile")
private final AtomicFile mSliceAccessFile;
+ @GuardedBy("mAccessList")
private final SliceFullAccessList mAccessList;
public SliceManagerService(Context context) {
@@ -127,11 +133,19 @@
InputStream input = mSliceAccessFile.openRead();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(input, Encoding.UTF_8.name());
- mAccessList.readXml(parser);
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
} catch (IOException | XmlPullParserException e) {
Slog.d(TAG, "Can't read slice access file", e);
}
}
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
}
/// ----- Lifecycle stuff -----
@@ -223,7 +237,9 @@
getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
"Slice granting requires MANAGE_SLICE_PERMISSIONS");
if (allSlices) {
- mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ synchronized (mAccessList) {
+ mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ }
mHandler.post(mSaveAccessList);
} else {
synchronized (mLock) {
@@ -244,7 +260,71 @@
}
}
+ // Backup/restore interface
+ @Override
+ public byte[] getBackupPayload(int user) {
+ if (Binder.getCallingUid() != SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ //TODO: http://b/22388012
+ if (user != UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
+ return null;
+ }
+ synchronized(mSliceAccessFile) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+ out.setOutput(baos, Encoding.UTF_8.name());
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out, user);
+ }
+ out.flush();
+ return baos.toByteArray();
+ } catch (IOException | XmlPullParserException e) {
+ Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void applyRestore(byte[] payload, int user) {
+ if (Binder.getCallingUid() != SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ if (payload == null) {
+ Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
+ return;
+ }
+ //TODO: http://b/22388012
+ if (user != UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
+ return;
+ }
+ synchronized(mSliceAccessFile) {
+ final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+ try {
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(bais, Encoding.UTF_8.name());
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
+ mHandler.post(mSaveAccessList);
+ } catch (NumberFormatException | XmlPullParserException | IOException e) {
+ Slog.w(TAG, "applyRestore: error reading payload", e);
+ }
+ }
+ }
+
/// ----- internal code -----
+ private void removeFullAccess(String pkg, int userId) {
+ synchronized (mAccessList) {
+ mAccessList.removeGrant(pkg, userId);
+ }
+ mHandler.post(mSaveAccessList);
+ }
+
protected void removePinnedSlice(Uri uri) {
synchronized (mLock) {
mPinnedSlicesByUri.remove(uri).destroy();
@@ -444,7 +524,9 @@
}
private boolean isGrantedFullAccess(String pkg, int userId) {
- return mAccessList.hasFullAccess(pkg, userId);
+ synchronized (mAccessList) {
+ return mAccessList.hasFullAccess(pkg, userId);
+ }
}
private static ServiceThread createHandler() {
@@ -469,7 +551,9 @@
try {
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(stream, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out, UserHandle.USER_ALL);
+ }
out.flush();
mSliceAccessFile.finishWrite(stream);
} catch (IOException | XmlPullParserException e) {
@@ -480,6 +564,35 @@
}
};
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
+ Uri data = intent.getData();
+ String pkg = data != null ? data.getSchemeSpecificPart() : null;
+ if (pkg == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ final boolean replacing =
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ removeFullAccess(pkg, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ removeFullAccess(pkg, userId);
+ break;
+ }
+ }
+ };
+
public static class Lifecycle extends SystemService {
private SliceManagerService mService;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f498cdd..ef6067a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -85,11 +85,9 @@
public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
- public static final String ACTION_TRIGGER_COLLECTION =
- "com.android.server.stats.action.TRIGGER_COLLECTION";
-
+ public static final int CODE_DATA_BROADCAST = 1;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
private final Context mContext;
@@ -110,10 +108,9 @@
private TelephonyManager mTelephony = null;
private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
private final StatFs mStatFsSystem =
- new StatFs(Environment.getRootDirectory().getAbsolutePath());
+ new StatFs(Environment.getRootDirectory().getAbsolutePath());
private final StatFs mStatFsTemp =
- new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
- private final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
public StatsCompanionService(Context context) {
super();
@@ -123,7 +120,7 @@
mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
mPullingAlarmIntent = PendingIntent.getBroadcast(
- mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
+ mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -154,17 +151,21 @@
for (int i = 0; i < numClusters; i++) {
final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
- numSpeedSteps);
+ numSpeedSteps);
firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
}
}
@Override
- public void sendBroadcast(String pkg, String cls) {
- // TODO: Use a pending intent.
+ public void sendDataBroadcast(IBinder intentSenderBinder) {
enforceCallingPermission();
- mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
- UserHandle.SYSTEM);
+ IntentSender intentSender = new IntentSender(intentSenderBinder);
+ Intent intent = new Intent();
+ try {
+ intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.w(TAG, "Unable to send using IntentSender");
+ }
}
@Override
@@ -210,6 +211,7 @@
}
// Assumes that sStatsdLock is held.
+ @GuardedBy("sStatsdLock")
private final void informAllUidsLocked(Context context) throws RemoteException {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
PackageManager pm = context.getPackageManager();
@@ -285,7 +287,7 @@
}
}
- private final static class AnomalyAlarmReceiver extends BroadcastReceiver {
+ public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
@@ -305,25 +307,24 @@
}
}
- private final static class PullingAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG)
- Slog.d(TAG, "Time to poll something.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informPollAlarmFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
- }
+ public final static class PullingAlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG)
+ Slog.d(TAG, "Time to poll something.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informPollAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
+ }
+ }
}
- // AlarmManager releases its own wakelock here.
- }
}
private final static class ShutdownEventReceiver extends BroadcastReceiver {
@@ -333,9 +334,9 @@
* Skip immediately if intent is not relevant to device shutdown.
*/
if (!intent.getAction().equals(Intent.ACTION_REBOOT)
- && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
- && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
- return;
+ && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
+ && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
+ return;
}
Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
@@ -382,50 +383,50 @@
@Override // Binder call
public void setPullingAlarms(long timestampMs, long intervalMs) {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
- // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
- // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
- mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
+ mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
@Override // Binder call
public void cancelPullingAlarms() {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Cancelling pulling alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling pulling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
private void addNetworkStats(
- int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
- int size = stats.size();
- NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
- for (int j = 0; j < size; j++) {
- stats.getValues(j, entry);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
- e.writeInt(entry.uid);
- if (withFGBG) {
- e.writeInt(entry.set);
+ int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
+ int size = stats.size();
+ NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+ for (int j = 0; j < size; j++) {
+ stats.getValues(j, entry);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
+ e.writeInt(entry.uid);
+ if (withFGBG) {
+ e.writeInt(entry.set);
+ }
+ e.writeLong(entry.rxBytes);
+ e.writeLong(entry.rxPackets);
+ e.writeLong(entry.txBytes);
+ e.writeLong(entry.txPackets);
+ ret.add(e);
}
- e.writeLong(entry.rxBytes);
- e.writeLong(entry.rxPackets);
- e.writeLong(entry.txBytes);
- e.writeLong(entry.txPackets);
- ret.add(e);
- }
}
/**
@@ -492,219 +493,220 @@
}
private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
- final KernelWakelockStats wakelockStats =
- mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
- for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
- String name = ent.getKey();
- KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
- e.writeString(name);
- e.writeInt(kws.mCount);
- e.writeInt(kws.mVersion);
- e.writeLong(kws.mTotalTime);
- pulledData.add(e);
- }
+ final KernelWakelockStats wakelockStats =
+ mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
+ e.writeString(name);
+ e.writeInt(kws.mCount);
+ e.writeInt(kws.mVersion);
+ e.writeLong(kws.mTotalTime);
+ pulledData.add(e);
+ }
}
private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- // TODO: Consider caching the following call to get BatteryStatsInternal.
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Consider caching the following call to get BatteryStatsInternal.
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats =
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ .groupedByUid();
+ addNetworkStats(tagId, pulledData, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- // Combine all the metrics per Uid into one record.
- NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
- .groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ addNetworkStats(tagId, pulledData, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats =
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ .groupedByUid();
+ addNetworkStats(tagId, pulledData, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- // Combine all the metrics per Uid into one record.
- NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
- .groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = pullBluetoothData();
- for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
- e.writeInt(traffic.getUid());
- e.writeLong(traffic.getRxBytes());
- e.writeLong(traffic.getTxBytes());
- pulledData.add(e);
- }
+ BluetoothActivityEnergyInfo info = pullBluetoothData();
+ for (UidTraffic traffic : info.getUidTraffic()) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeInt(traffic.getUid());
+ e.writeLong(traffic.getRxBytes());
+ e.writeLong(traffic.getTxBytes());
+ pulledData.add(e);
+ }
}
private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ addNetworkStats(tagId, pulledData, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
- for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
- long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
- if (clusterTimeMs != null) {
- for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
- e.writeInt(cluster);
- e.writeInt(speed);
- e.writeLong(clusterTimeMs[speed]);
- pulledData.add(e);
- }
+ for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+ long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
+ if (clusterTimeMs != null) {
+ for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeInt(cluster);
+ e.writeInt(speed);
+ e.writeLong(clusterTimeMs[speed]);
+ pulledData.add(e);
+ }
+ }
}
- }
}
private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- if (mWifiManager == null) {
- mWifiManager =
- IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
- }
- if (mWifiManager != null) {
- try {
- SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
- final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
- e.writeLong(wifiInfo.getTimeStamp());
- e.writeInt(wifiInfo.getStackState());
- e.writeLong(wifiInfo.getControllerTxTimeMillis());
- e.writeLong(wifiInfo.getControllerRxTimeMillis());
- e.writeLong(wifiInfo.getControllerIdleTimeMillis());
- e.writeLong(wifiInfo.getControllerEnergyUsed());
- pulledData.add(e);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
+ long token = Binder.clearCallingIdentity();
+ if (mWifiManager == null) {
+ mWifiManager =
+ IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
}
- }
+ if (mWifiManager != null) {
+ try {
+ SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.requestActivityInfo(wifiReceiver);
+ final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ e.writeLong(wifiInfo.getTimeStamp());
+ e.writeInt(wifiInfo.getStackState());
+ e.writeLong(wifiInfo.getControllerTxTimeMillis());
+ e.writeLong(wifiInfo.getControllerRxTimeMillis());
+ e.writeLong(wifiInfo.getControllerIdleTimeMillis());
+ e.writeLong(wifiInfo.getControllerEnergyUsed());
+ pulledData.add(e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- if (mTelephony == null) {
- mTelephony = TelephonyManager.from(mContext);
- }
- if (mTelephony != null) {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
- e.writeLong(modemInfo.getTimestamp());
- e.writeLong(modemInfo.getSleepTimeMillis());
- e.writeLong(modemInfo.getIdleTimeMillis());
- e.writeLong(modemInfo.getTxTimeMillis()[0]);
- e.writeLong(modemInfo.getTxTimeMillis()[1]);
- e.writeLong(modemInfo.getTxTimeMillis()[2]);
- e.writeLong(modemInfo.getTxTimeMillis()[3]);
- e.writeLong(modemInfo.getTxTimeMillis()[4]);
- e.writeLong(modemInfo.getRxTimeMillis());
- e.writeLong(modemInfo.getEnergyUsed());
- pulledData.add(e);
- }
+ long token = Binder.clearCallingIdentity();
+ if (mTelephony == null) {
+ mTelephony = TelephonyManager.from(mContext);
+ }
+ if (mTelephony != null) {
+ SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
+ mTelephony.requestModemActivityInfo(modemReceiver);
+ final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ e.writeLong(modemInfo.getTimestamp());
+ e.writeLong(modemInfo.getSleepTimeMillis());
+ e.writeLong(modemInfo.getIdleTimeMillis());
+ e.writeLong(modemInfo.getTxTimeMillis()[0]);
+ e.writeLong(modemInfo.getTxTimeMillis()[1]);
+ e.writeLong(modemInfo.getTxTimeMillis()[2]);
+ e.writeLong(modemInfo.getTxTimeMillis()[3]);
+ e.writeLong(modemInfo.getTxTimeMillis()[4]);
+ e.writeLong(modemInfo.getRxTimeMillis());
+ e.writeLong(modemInfo.getEnergyUsed());
+ pulledData.add(e);
+ }
}
private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = pullBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
- e.writeLong(info.getTimeStamp());
- e.writeInt(info.getBluetoothStackState());
- e.writeLong(info.getControllerTxTimeMillis());
- e.writeLong(info.getControllerRxTimeMillis());
- e.writeLong(info.getControllerIdleTimeMillis());
- e.writeLong(info.getControllerEnergyUsed());
- pulledData.add(e);
+ BluetoothActivityEnergyInfo info = pullBluetoothData();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ e.writeLong(info.getTimeStamp());
+ e.writeInt(info.getBluetoothStackState());
+ e.writeLong(info.getControllerTxTimeMillis());
+ e.writeLong(info.getControllerRxTimeMillis());
+ e.writeLong(info.getControllerIdleTimeMillis());
+ e.writeLong(info.getControllerEnergyUsed());
+ pulledData.add(e);
}
private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
- if (adapter != null) {
- SynchronousResultReceiver bluetoothReceiver = null;
- bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
- adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
- return awaitControllerInfo(bluetoothReceiver);
- } else {
- return null;
- }
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
+ adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+ return awaitControllerInfo(bluetoothReceiver);
+ } else {
+ Slog.e(TAG, "Failed to get bluetooth adapter!");
+ return null;
+ }
}
private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
- e.writeLong(SystemClock.elapsedRealtime());
- pulledData.add(e);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.elapsedRealtime());
+ pulledData.add(e);
}
private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
- e.writeLong(mStatFsData.getAvailableBytes());
- e.writeLong(mStatFsSystem.getAvailableBytes());
- e.writeLong(mStatFsTemp.getAvailableBytes());
- pulledData.add(e);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeLong(mStatFsData.getAvailableBytes());
+ e.writeLong(mStatFsSystem.getAvailableBytes());
+ e.writeLong(mStatFsTemp.getAvailableBytes());
+ pulledData.add(e);
}
private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
- e.writeLong(SystemClock.uptimeMillis());
- pulledData.add(e);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.uptimeMillis());
+ pulledData.add(e);
}
/**
@@ -718,56 +720,56 @@
List<StatsLogEventWrapper> ret = new ArrayList();
switch (tagId) {
case StatsLog.WIFI_BYTES_TRANSFER: {
- pullWifiBytesTransfer(tagId, ret);
- break;
+ pullWifiBytesTransfer(tagId, ret);
+ break;
}
case StatsLog.MOBILE_BYTES_TRANSFER: {
- pullMobileBytesTransfer(tagId, ret);
- break;
+ pullMobileBytesTransfer(tagId, ret);
+ break;
}
case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
- pullWifiBytesTransferByFgBg(tagId, ret);
- break;
+ pullWifiBytesTransferByFgBg(tagId, ret);
+ break;
}
case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
- pullMobileBytesTransferByFgBg(tagId, ret);
- break;
+ pullMobileBytesTransferByFgBg(tagId, ret);
+ break;
}
case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
- pullBluetoothBytesTransfer(tagId, ret);
- break;
+ pullBluetoothBytesTransfer(tagId, ret);
+ break;
}
case StatsLog.KERNEL_WAKELOCK: {
- pullKernelWakelock(tagId, ret);
- break;
+ pullKernelWakelock(tagId, ret);
+ break;
}
case StatsLog.CPU_TIME_PER_FREQ: {
- pullCpuTimePerFreq(tagId, ret);
- break;
+ pullCpuTimePerFreq(tagId, ret);
+ break;
}
case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
- pullWifiActivityEnergyInfo(tagId, ret);
- break;
+ pullWifiActivityEnergyInfo(tagId, ret);
+ break;
}
case StatsLog.MODEM_ACTIVITY_INFO: {
- pullModemActivityInfo(tagId, ret);
- break;
+ pullModemActivityInfo(tagId, ret);
+ break;
}
case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
- pullBluetoothActivityInfo(tagId, ret);
- break;
+ pullBluetoothActivityInfo(tagId, ret);
+ break;
}
case StatsLog.SYSTEM_UPTIME: {
- pullSystemUpTime(tagId, ret);
- break;
+ pullSystemUpTime(tagId, ret);
+ break;
}
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
- pullSystemElapsedRealtime(tagId, ret);
- break;
+ pullSystemElapsedRealtime(tagId, ret);
+ break;
}
case StatsLog.DISK_SPACE: {
- pullDiskSpace(tagId, ret);
- break;
+ pullDiskSpace(tagId, ret);
+ break;
}
default:
Slog.w(TAG, "No such tagId data as " + tagId);
@@ -788,14 +790,14 @@
@Override
public void triggerUidSnapshot() {
- enforceCallingPermission();
- synchronized (sStatsdLock) {
- try {
- informAllUidsLocked(mContext);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+ enforceCallingPermission();
+ synchronized (sStatsdLock) {
+ try {
+ informAllUidsLocked(mContext);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+ }
}
- }
}
private void enforceCallingPermission() {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 7c170ae..343fb91 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -321,7 +321,7 @@
public void showChargingAnimation(int batteryLevel) {
if (mBar != null) {
try {
- mBar.showChargingAnimation(batteryLevel);
+ mBar.showWirelessChargingAnimation(batteryLevel);
} catch (RemoteException ex){
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 853c7eb..0ac853b 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -43,7 +42,6 @@
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
/**
* A manager for TextClassifier services.
@@ -54,9 +52,6 @@
private static final String LOG_TAG = "TextClassificationManagerService";
- // How long after the last interaction with the service we would unbind
- private static final long TIMEOUT_IDLE_BIND_MILLIS = TimeUnit.MINUTES.toMillis(1);
-
public static final class Lifecycle extends SystemService {
private final TextClassificationManagerService mManagerService;
@@ -79,10 +74,8 @@
}
private final Context mContext;
- private final Handler mHandler;
private final Intent mServiceIntent;
private final ServiceConnection mConnection;
- private final Runnable mUnbind;
private final Object mLock;
@GuardedBy("mLock")
private final Queue<PendingRequest> mPendingRequests;
@@ -94,7 +87,6 @@
private TextClassificationManagerService(Context context) {
mContext = Preconditions.checkNotNull(context);
- mHandler = new Handler();
mServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
.setComponent(TextClassifierService.getServiceComponentName(mContext));
mConnection = new ServiceConnection() {
@@ -131,7 +123,6 @@
}
};
mPendingRequests = new LinkedList<>();
- mUnbind = this::unbind;
mLock = new Object();
}
@@ -152,7 +143,6 @@
if (isBoundLocked()) {
mService.onSuggestSelection(
text, selectionStartIndex, selectionEndIndex, options, callback);
- scheduleUnbindLocked();
} else {
final Callable<Void> request = () -> {
onSuggestSelection(
@@ -184,7 +174,6 @@
synchronized (mLock) {
if (isBoundLocked()) {
mService.onClassifyText(text, startIndex, endIndex, options, callback);
- scheduleUnbindLocked();
} else {
final Callable<Void> request = () -> {
onClassifyText(text, startIndex, endIndex, options, callback);
@@ -213,7 +202,6 @@
synchronized (mLock) {
if (isBoundLocked()) {
mService.onGenerateLinks(text, options, callback);
- scheduleUnbindLocked();
} else {
final Callable<Void> request = () -> {
onGenerateLinks(text, options, callback);
@@ -270,27 +258,6 @@
mBinding = binding;
}
- private void unbind() {
- synchronized (mLock) {
- if (!isBoundLocked()) {
- return;
- }
-
- Slog.d(LOG_TAG, "Unbinding from " + mServiceIntent.getComponent());
- mContext.unbindService(mConnection);
-
- synchronized (mLock) {
- mService = null;
- }
- }
- }
-
- @GuardedBy("mLock")
- private void scheduleUnbindLocked() {
- mHandler.removeCallbacks(mUnbind);
- mHandler.postDelayed(mUnbind, TIMEOUT_IDLE_BIND_MILLIS);
- }
-
@GuardedBy("mLock")
private void enqueueRequestLocked(
Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) {
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 3f32079..b00e595 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -72,20 +72,23 @@
}
/** Cancels the notification */
- void cancel() {
+ void cancel(boolean deleteChannel) {
// We can't call into NotificationManager with WM lock held since it might call into AM.
// So, we post a message to do it later.
- mService.mH.post(this::onCancelNotification);
+ mService.mH.post(() -> onCancelNotification(deleteChannel));
}
/** Don't call with the window manager lock held! */
- private void onCancelNotification() {
+ private void onCancelNotification(boolean deleteChannel) {
if (!mPosted) {
// Notification isn't currently posted...
return;
}
mPosted = false;
mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
+ if (deleteChannel) {
+ mNotificationManager.deleteNotificationChannel(mNotificationTag);
+ }
}
/** Don't call with the window manager lock held! */
@@ -146,8 +149,12 @@
final String nameChannel =
context.getString(R.string.alert_windows_notification_channel_name, appName);
- final NotificationChannel channel =
- new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
+
+ NotificationChannel channel = mNotificationManager.getNotificationChannel(mNotificationTag);
+ if (channel != null) {
+ return;
+ }
+ channel = new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
channel.enableLights(false);
channel.enableVibration(false);
channel.setBlockableSystem(true);
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index b435605..4394a99 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.graphics.Rect;
@@ -124,17 +123,30 @@
}
}
- @VisibleForTesting
- ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
-
/**
* The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
* host, some controller of it, or one of the hosts children.
*/
private WindowContainer mHost;
+ private WindowContainer mLastRequestedDimContainer;
+ @VisibleForTesting
+ DimState mDimState;
+
+ private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
+
+ @VisibleForTesting
+ interface SurfaceAnimatorStarter {
+ void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
+ AnimationAdapter anim, boolean hidden);
+ }
Dimmer(WindowContainer host) {
+ this(host, SurfaceAnimator::startAnimation);
+ }
+
+ Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
mHost = host;
+ mSurfaceAnimatorStarter = surfaceAnimatorStarter;
}
private SurfaceControl makeDimLayer() {
@@ -146,29 +158,32 @@
}
/**
- * Retreive the DimState for a given child of the host.
+ * Retrieve the DimState, creating one if it doesn't exist.
*/
private DimState getDimState(WindowContainer container) {
- DimState state = mDimLayerUsers.get(container);
- if (state == null) {
+ if (mDimState == null) {
final SurfaceControl ctl = makeDimLayer();
- state = new DimState(ctl);
+ mDimState = new DimState(ctl);
/**
* See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
* via state resetting for Dim's with containers.
*/
if (container == null) {
- state.mDontReset = true;
+ mDimState.mDontReset = true;
}
- mDimLayerUsers.put(container, state);
}
- return state;
+
+ mLastRequestedDimContainer = container;
+ return mDimState;
}
private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
float alpha) {
final DimState d = getDimState(container);
if (container != null) {
+ // The dim method is called from WindowState.prepareSurfaces(), which is always called
+ // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
+ // relative to the highest Z layer with a dim.
t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
} else {
t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
@@ -237,11 +252,8 @@
* a chance to request dims to continue.
*/
void resetDimStates() {
- for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
- final DimState state = mDimLayerUsers.valueAt(i);
- if (!state.mDontReset) {
- state.mDimming = false;
- }
+ if (mDimState != null && !mDimState.mDontReset) {
+ mDimState.mDimming = false;
}
}
@@ -254,30 +266,25 @@
* @return true if any Dims were updated.
*/
boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
- boolean didSomething = false;
- for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
- DimState state = mDimLayerUsers.valueAt(i);
- WindowContainer container = mDimLayerUsers.keyAt(i);
-
- // TODO: We want to animate the addition and removal of Dim's instead of immediately
- // acting. When we do this we need to take care to account for the "Replacing Windows"
- // case (and seamless dim transfer).
- if (!state.mDimming) {
- mDimLayerUsers.removeAt(i);
- startDimExit(container, state.mSurfaceAnimator, t);
- } else {
- didSomething = true;
- // TODO: Once we use geometry from hierarchy this falls away.
- t.setSize(state.mDimLayer, bounds.width(), bounds.height());
- t.setPosition(state.mDimLayer, bounds.left, bounds.top);
- if (!state.isVisible) {
- state.isVisible = true;
- t.show(state.mDimLayer);
- startDimEnter(container, state.mSurfaceAnimator, t);
- }
- }
+ if (mDimState == null) {
+ return false;
}
- return didSomething;
+
+ if (!mDimState.mDimming) {
+ startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
+ mDimState = null;
+ return false;
+ } else {
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
+ t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
+ if (!mDimState.isVisible) {
+ mDimState.isVisible = true;
+ t.show(mDimState.mDimLayer);
+ startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
+ }
+ return true;
+ }
}
private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
@@ -292,7 +299,7 @@
private void startAnim(WindowContainer container, SurfaceAnimator animator,
SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
- animator.startAnimation(t, new LocalAnimationAdapter(
+ mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7674b5e..2512dbd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -662,7 +662,7 @@
mWallpaperController.updateWallpaperVisibility();
}
- w.handleWindowMovedIfNeeded(mPendingTransaction);
+ w.handleWindowMovedIfNeeded();
final WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -1542,11 +1542,11 @@
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
+ void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.getChildAt(i);
if (stack.updateBoundsAfterConfigChange()) {
- changedStackList.add(stack.mStackId);
+ changedStackList.add(stack);
}
}
@@ -3599,8 +3599,6 @@
}
private final class AboveAppWindowContainers extends NonAppWindowContainers {
- private final Dimmer mDimmer = new Dimmer(this);
- private final Rect mTmpDimBoundsRect = new Rect();
AboveAppWindowContainers(String name, WindowManagerService service) {
super(name, service);
}
@@ -3632,22 +3630,6 @@
imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
}
}
-
- @Override
- Dimmer getDimmer() {
- return mDimmer;
- }
-
- @Override
- void prepareSurfaces() {
- mDimmer.resetDimStates();
- super.prepareSurfaces();
- getBounds(mTmpDimBoundsRect);
-
- if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
- scheduleAnimation();
- }
- }
}
/**
@@ -3679,6 +3661,9 @@
};
private final String mName;
+ private final Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
NonAppWindowContainers(String name, WindowManagerService service) {
super(service);
mName = name;
@@ -3722,6 +3707,22 @@
String getName() {
return mName;
}
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getBounds(mTmpDimBoundsRect);
+
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
}
private class NonMagnifiableWindowContainers extends NonAppWindowContainers {
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index d55a649..fc370d9 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -153,9 +153,9 @@
mDragState.mToken = dragToken;
final Display display = displayContent.getDisplay();
- mDragState.register(display);
- if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
- mDragState.getInputChannel())) {
+ if (!mCallback.get().registerInputChannel(
+ mDragState, display, mService.mInputManager,
+ callingWin.mInputChannel)) {
Slog.e(TAG_WM, "Unable to transfer touch focus");
return null;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index fe5b65c..e869f58 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,6 +62,7 @@
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mHomeAppToken;
+ private Rect mMinimizedHomeBounds = new Rect();
// We start the RecentsAnimationController in a pending-start state since we need to wait for
// the wallpaper/activity to draw before we can give control to the handler to start animating
@@ -105,7 +106,7 @@
final AppWindowToken topChild = task.getTopChild();
final WindowState mainWindow = topChild.findMainWindow();
return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
- mainWindow.mStableInsets,
+ mainWindow.mContentInsets,
ActivityManager.isLowRamDeviceStatic() /* reduced */,
1.0f /* scale */);
}
@@ -163,8 +164,6 @@
* @param remoteAnimationRunner The remote runner which should be notified when the animation is
* ready to start or has been canceled
* @param callbacks Callbacks to be made when the animation finishes
- * @param restoreHomeBehindStackId The stack id to restore the home stack behind once the
- * animation is complete. Will be passed to the callback.
*/
RecentsAnimationController(WindowManagerService service,
IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
@@ -200,13 +199,15 @@
if (recentsComponentAppToken != null) {
if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
mHomeAppToken = recentsComponentAppToken;
- final WallpaperController wc = dc.mWallpaperController;
if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
dc.setLayoutNeeded();
}
}
+ // Save the minimized home height
+ dc.getDockedDividerController().getHomeStackBoundsInDockedMode(mMinimizedHomeBounds);
+
mService.mWindowPlacerLocked.performSurfacePlacement();
}
@@ -232,7 +233,15 @@
appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
}
mPendingStart = false;
- mRunner.onAnimationStart(mController, appAnimations);
+
+ final Rect minimizedHomeBounds =
+ mHomeAppToken != null && mHomeAppToken.inSplitScreenSecondaryWindowingMode()
+ ? mMinimizedHomeBounds : null;
+ final Rect contentInsets =
+ mHomeAppToken != null && mHomeAppToken.findMainWindow() != null
+ ? mHomeAppToken.findMainWindow().mContentInsets : null;
+ mRunner.onAnimationStart_New(mController, appAnimations, contentInsets,
+ minimizedHomeBounds);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
@@ -334,11 +343,15 @@
}
RemoteAnimationTarget createRemoteAnimationApp() {
- // TODO: Do we need position and stack bounds?
+ final Point position = new Point();
+ final Rect bounds = new Rect();
+ final WindowContainer container = mTask.getParent();
+ container.getRelativePosition(position);
+ container.getBounds(bounds);
+ final WindowState mainWindow = mTask.getTopVisibleAppMainWindow();
return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
- !mTask.fillsParent(),
- mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect,
- mTask.getPrefixOrderIndex(), new Point(), new Rect(),
+ !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
+ mainWindow.mContentInsets, mTask.getPrefixOrderIndex(), position, bounds,
mTask.getWindowConfiguration());
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8269a3b..c353c1d 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -92,7 +92,10 @@
onAnimationFinished();
return;
}
- mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
+
+ // Scale the timeout with the animator scale the controlling app is using.
+ mHandler.postDelayed(mTimeoutRunnable,
+ (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
try {
mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
mFinishedCallback);
@@ -163,7 +166,7 @@
}
return new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
- mainWindow.mWinAnimator.mLastClipRect,
+ mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
task.getWindowConfiguration());
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 68c8995..c535fe5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -47,6 +47,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -126,7 +127,8 @@
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
- private final ArrayList<Integer> mChangedStackList = new ArrayList();
+ private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
+ private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
// State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
// instances will be replaced with an instance that writes a binary representation of all
@@ -333,7 +335,8 @@
/**
* Set new display override config and return array of ids of stacks that were changed during
- * update. If called for the default display, global configuration will also be updated.
+ * update. If called for the default display, global configuration will also be updated. Stacks
+ * that are marked for deferred removal are excluded from the returned array.
*/
int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
final DisplayContent displayContent = getDisplayContent(displayId);
@@ -346,24 +349,42 @@
if (!configChanged) {
return null;
}
+
displayContent.onOverrideConfigurationChanged(newConfiguration);
+ mTmpStackList.clear();
if (displayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config. In this case
// we also want to update the global config.
- return setGlobalConfigurationIfNeeded(newConfiguration);
+ setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList);
} else {
- return updateStackBoundsAfterConfigChange(displayId);
+ updateStackBoundsAfterConfigChange(displayId, mTmpStackList);
}
+
+ mTmpStackIds.clear();
+ final int stackCount = mTmpStackList.size();
+
+ for (int i = 0; i < stackCount; ++i) {
+ final TaskStack stack = mTmpStackList.get(i);
+
+ // We only include stacks that are not marked for removal as they do not exist outside
+ // of WindowManager at this point.
+ if (!stack.mDeferRemoval) {
+ mTmpStackIds.add(stack.mStackId);
+ }
+ }
+
+ return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds);
}
- private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+ private void setGlobalConfigurationIfNeeded(Configuration newConfiguration,
+ List<TaskStack> changedStacks) {
final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
if (!configChanged) {
- return null;
+ return;
}
onConfigurationChanged(newConfiguration);
- return updateStackBoundsAfterConfigChange();
+ updateStackBoundsAfterConfigChange(changedStacks);
}
@Override
@@ -378,26 +399,18 @@
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- private int[] updateStackBoundsAfterConfigChange() {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) {
final int numDisplays = mChildren.size();
for (int i = 0; i < numDisplays; ++i) {
final DisplayContent dc = mChildren.get(i);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
}
/** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
- private int[] updateStackBoundsAfterConfigChange(int displayId) {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
final DisplayContent dc = getDisplayContent(displayId);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
private void prepareFreezingTaskBounds() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 04ae38e..2c3c5d2 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -55,6 +55,7 @@
import android.view.SurfaceSession;
import android.view.WindowManager;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
@@ -508,8 +509,14 @@
// on-going notification for the user to control their visibility.
if (visible) {
changed = mAlertWindowSurfaces.add(surfaceController);
+ if (changed) {
+ MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, true);
+ }
} else {
changed = mAlertWindowSurfaces.remove(surfaceController);
+ if (changed) {
+ MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, true);
+ }
}
if (changed) {
@@ -530,8 +537,14 @@
if (visible) {
changed = mAppOverlaySurfaces.add(surfaceController);
+ if (changed) {
+ MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, false);
+ }
} else {
changed = mAppOverlaySurfaces.remove(surfaceController);
+ if (changed) {
+ MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, false);
+ }
}
if (changed) {
@@ -547,7 +560,7 @@
if (allowed) {
mAlertWindowNotification.post();
} else {
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(false /* deleteChannel */);
}
}
}
@@ -586,7 +599,7 @@
if (mAlertWindowNotification == null) {
return;
}
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(true /* deleteChannel */);
mAlertWindowNotification = null;
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index dc62cc8..1b2f954 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -151,6 +151,7 @@
}
}
+ @GuardedBy("mLock")
private void startPendingAnimationsLocked() {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
startAnimationLocked(mPendingAnimations.valueAt(i));
@@ -158,6 +159,7 @@
mPendingAnimations.clear();
}
+ @GuardedBy("mLock")
private void startAnimationLocked(RunningAnimation a) {
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 0512a08..83baee1 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -53,7 +53,8 @@
SurfaceControl mLeash;
private final Animatable mAnimatable;
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
- private final Runnable mAnimationFinishedCallback;
+ @VisibleForTesting
+ final Runnable mAnimationFinishedCallback;
private boolean mAnimationStartDelayed;
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 212a0d70..a7a2b53 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -27,6 +27,7 @@
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Environment;
@@ -40,6 +41,7 @@
import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
@@ -324,7 +326,8 @@
if (mainWindow == null) {
return null;
}
- final int color = task.getTaskDescription().getBackgroundColor();
+ final int color = ColorUtils.setAlphaComponent(
+ task.getTaskDescription().getBackgroundColor(), 255);
final int statusBarColor = task.getTaskDescription().getStatusBarColor();
final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
final LayoutParams attrs = mainWindow.getAttrs();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 7b047a8..621bee7 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -281,11 +281,13 @@
mSnapshot = snapshot;
}
+ @GuardedBy("mLock")
@Override
void onQueuedLocked() {
mStoreQueueItems.offer(this);
}
+ @GuardedBy("mLock")
@Override
void onDequeuedLocked() {
mStoreQueueItems.remove(this);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 259f8df..e4db3b0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -47,6 +47,7 @@
import android.app.ActivityThread;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -516,7 +517,7 @@
@VisibleForTesting
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
int statusBarHeight) {
- if (statusBarHeight > 0
+ if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
mContentInsets.right);
@@ -531,7 +532,7 @@
getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
navigationBarRect);
final boolean visible = isNavigationBarColorViewVisible();
- if (visible && !navigationBarRect.isEmpty()) {
+ if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
c.drawRect(navigationBarRect, mNavigationBarPaint);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1935a44..6bceda5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -23,12 +23,14 @@
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
import android.os.IBinder;
+import android.view.Display;
import android.view.IInputFilter;
import android.view.IWindow;
+import android.view.InputChannel;
import android.view.MagnificationSpec;
import android.view.WindowInfo;
-import android.view.animation.Animation;
+import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import java.util.List;
@@ -153,6 +155,13 @@
* An interface to customize drag and drop behaviors.
*/
public interface IDragDropCallback {
+ default boolean registerInputChannel(
+ DragState state, Display display, InputManagerService service,
+ InputChannel source) {
+ state.register(display);
+ return service.transferTouchFocus(source, state.getInputChannel());
+ }
+
/**
* Called when drag operation is starting.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 066e4e6..676fb9f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,8 +24,6 @@
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -125,7 +123,6 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
-import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -907,16 +904,9 @@
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
- return main(context, im, haveInputMethods, showBootMsgs, onlyCore, policy,
- new SurfaceAnimationRunner());
- }
-
- public static WindowManagerService main(final Context context, final InputManagerService im,
- final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
- WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
- onlyCore, policy, surfaceAnimationRunner), 0);
+ onlyCore, policy), 0);
return sInstance;
}
@@ -939,7 +929,7 @@
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
- WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) {
+ WindowManagerPolicy policy) {
installLock(this, INDEX_WINDOW);
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -1066,7 +1056,7 @@
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
mHoldingScreenWakeLock.setReferenceCounted(false);
- mSurfaceAnimationRunner = surfaceAnimationRunner;
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner();
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
@@ -2466,6 +2456,7 @@
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
+
return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 36e3612..b6712c0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -265,7 +265,7 @@
// This is a non-system overlay window that is currently force hidden.
private boolean mForceHideNonSystemOverlayWindow;
boolean mAppFreezing;
- boolean mHidden; // Used to determine if to show child windows.
+ boolean mHidden = true; // Used to determine if to show child windows.
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
private boolean mDragResizing;
private boolean mDragResizingChangeReported = true;
@@ -1758,7 +1758,7 @@
* listeners and optionally animate it. Simply checking a change of position is not enough,
* because being move due to dock divider is not a trigger for animation.
*/
- void handleWindowMovedIfNeeded(Transaction t) {
+ void handleWindowMovedIfNeeded() {
if (!hasMoved()) {
return;
}
@@ -1776,7 +1776,7 @@
&& !isDragResizing() && !adjustedForMinimizedDockOrIme
&& getWindowConfiguration().hasMovementAnimations()
&& !mWinAnimator.mLastHidden) {
- startMoveAnimation(t, left, top);
+ startMoveAnimation(left, top);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -4360,7 +4360,7 @@
commitPendingTransaction();
}
- private void startMoveAnimation(Transaction t, int left, int top) {
+ private void startMoveAnimation(int left, int top) {
if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
@@ -4369,7 +4369,7 @@
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
mService.mSurfaceAnimationRunner);
- startAnimation(t, adapter);
+ startAnimation(getPendingTransaction(), adapter);
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
@@ -4505,8 +4505,12 @@
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
dimmer.dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
- } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
- && !mAnimatingExit && isVisible() && !mWinAnimator.mLastHidden) {
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isVisibleNow() && !mHidden) {
+ // Only show a dim behind when the following is satisfied:
+ // 1. The window has the flag FLAG_DIM_BEHIND
+ // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
+ // 3. The WS is considered visible according to the isVisible() method
+ // 4. The WS is not hidden.
mIsDimming = true;
dimmer.dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5c9cfbb..dd23b6f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -211,6 +211,8 @@
private final Rect mTmpSize = new Rect();
+ private final SurfaceControl.Transaction mReparentTransaction = new SurfaceControl.Transaction();
+
WindowStateAnimator(final WindowState win) {
final WindowManagerService service = win.mService;
@@ -371,9 +373,9 @@
// child layers need to be reparented to the new surface to make this
// transparent to the app.
if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) {
- SurfaceControl.openTransaction();
- mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController);
- SurfaceControl.closeTransaction();
+ mReparentTransaction.reparentChildren(mPendingDestroySurface.mSurfaceControl,
+ mSurfaceController.mSurfaceControl.getHandle())
+ .apply();
}
}
}
@@ -587,7 +589,7 @@
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
WindowManagerService.logSurface(mWin, "DESTROY PENDING", true);
}
- mPendingDestroySurface.destroyInTransaction();
+ mPendingDestroySurface.destroyNotInTransaction();
}
mPendingDestroySurface = mSurfaceController;
}
@@ -624,7 +626,7 @@
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
WindowManagerService.logSurface(mWin, "DESTROY PENDING", true);
}
- mPendingDestroySurface.destroyInTransaction();
+ mPendingDestroySurface.destroyNotInTransaction();
// Don't hide wallpaper if we're destroying a deferred surface
// after a surface mode change.
if (!mDestroyPreservedSurfaceUponRedraw) {
@@ -1419,7 +1421,7 @@
void destroySurface() {
try {
if (mSurfaceController != null) {
- mSurfaceController.destroyInTransaction();
+ mSurfaceController.destroyNotInTransaction();
}
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying surface " + this
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 2f38556..554a600 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -168,7 +168,7 @@
}
}
- void destroyInTransaction() {
+ void destroyNotInTransaction() {
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(8));
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7540e26..5e003ff 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -24,6 +24,7 @@
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
"com_android_server_ConsumerIrService.cpp",
+ "com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
"com_android_server_hdmi_HdmiCecController.cpp",
"com_android_server_input_InputApplicationHandle.cpp",
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 8c38e0a..847222a 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -395,7 +395,8 @@
gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
- jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), jVendorInfo.get()));
+ jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr,
+ jVendorInfo.get()));
}
JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -712,7 +713,7 @@
gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
"(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
- "[I[ILjava/util/Map;)V");
+ "[I[ILjava/util/Map;Ljava/util/Map;)V");
auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass);
diff --git a/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
new file mode 100644
index 0000000..b53ea92
--- /dev/null
+++ b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
@@ -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.
+ */
+
+#include "jni.h"
+#include "core_jni_helpers.h"
+
+#include <openssl/crypto.h>
+
+namespace {
+
+static jint runSelfTest(JNIEnv* env, jobject /* clazz */) {
+ return BORINGSSL_self_test();
+}
+
+static const JNINativeMethod methods[] = {
+ /* name, signature, funcPtr */
+ {"runSelfTest", "()I", (void*) runSelfTest}
+};
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv *env) {
+ return jniRegisterNativeMethods(
+ env, "com/android/server/devicepolicy/CryptoTestHelper", methods, NELEM(methods));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 07ddb05..bf2a637 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -42,6 +42,7 @@
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
+int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
@@ -88,6 +89,7 @@
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
+ register_android_server_devicepolicy_CryptoTestHelper(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 9fcf3ee..3557dc9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -122,16 +122,6 @@
}
@Override
- public void setPrintingEnabled(ComponentName admin, boolean enabled) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isPrintingEnabled() {
- return true;
- }
-
- @Override
public List<String> setMeteredDataDisabled(ComponentName admin, List<String> packageNames) {
return packageNames;
}
@@ -177,4 +167,9 @@
String packageName, int userId) {
return false;
}
+
+ @Override
+ public long forceSecurityLogs() {
+ return 0;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java
new file mode 100644
index 0000000..a20758e
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java
@@ -0,0 +1,30 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.SecurityLog;
+
+/**
+ * Helper to call native BoringSSL self test.
+ */
+public class CryptoTestHelper {
+ public static void runAndLogSelfTest() {
+ final int result = runSelfTest();
+ SecurityLog.writeEvent(SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED, result);
+ }
+ private static native int runSelfTest();
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 60f204d..0c0ce8d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -193,6 +193,7 @@
}
}
+ @GuardedBy("mLock")
private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
final DevicePolicyServiceConnection conn = mConnections.get(userId);
if (conn != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4c57f7f..8753344 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -305,8 +305,6 @@
private static final String TAG_PASSWORD_VALIDITY = "password-validity";
- private static final String TAG_PRINTING_ENABLED = "printing-enabled";
-
private static final String TAG_TRANSFER_OWNERSHIP_BUNDLE = "transfer-ownership-bundle";
private static final int REQUEST_EXPIRE_PASSWORD = 5571;
@@ -615,8 +613,6 @@
long mPasswordTokenHandle = 0;
- boolean mPrintingEnabled = true;
-
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -2048,6 +2044,10 @@
public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
return new TransferOwnershipMetadataManager();
}
+
+ public void runCryptoSelfTest() {
+ CryptoTestHelper.runAndLogSelfTest();
+ }
}
/**
@@ -2300,6 +2300,7 @@
if (hasDeviceOwner && mInjector.securityLogGetLoggingEnabledProperty()) {
mSecurityLogMonitor.start();
+ mInjector.runCryptoSelfTest();
maybePauseDeviceWideLoggingLocked();
}
}
@@ -2948,12 +2949,6 @@
out.endTag(null, TAG_CURRENT_INPUT_METHOD_SET);
}
- if (!policy.mPrintingEnabled) {
- out.startTag(null, TAG_PRINTING_ENABLED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(policy.mPrintingEnabled));
- out.endTag(null, TAG_PRINTING_ENABLED);
- }
-
for (final String cert : policy.mOwnerInstalledCaCerts) {
out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT);
out.attribute(null, ATTR_ALIAS, cert);
@@ -3172,9 +3167,6 @@
policy.mCurrentInputMethodSet = true;
} else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
- } else if (TAG_PRINTING_ENABLED.equals(tag)) {
- String enabled = parser.getAttributeValue(null, ATTR_VALUE);
- policy.mPrintingEnabled = Boolean.toString(true).equals(enabled);
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -10234,6 +10226,7 @@
mInjector.registerContentObserver(mDefaultImeChanged, false, this, UserHandle.USER_ALL);
}
+ @GuardedBy("DevicePolicyManagerService.this")
private void addPendingChangeByOwnerLocked(int userId) {
mUserIdsWithPendingChangesByOwner.add(userId);
}
@@ -10417,7 +10410,7 @@
public CharSequence getPrintingDisabledReasonForUser(@UserIdInt int userId) {
synchronized (DevicePolicyManagerService.this) {
DevicePolicyData policy = getUserData(userId);
- if (policy.mPrintingEnabled) {
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING)) {
Log.e(LOG_TAG, "printing is enabled");
return null;
}
@@ -11659,6 +11652,15 @@
return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
}
+ @Override
+ public long forceSecurityLogs() {
+ enforceShell("forceSecurityLogs");
+ if (!mInjector.securityLogGetLoggingEnabledProperty()) {
+ throw new IllegalStateException("logging is not available");
+ }
+ return mSecurityLogMonitor.forceLogs();
+ }
+
private void enforceCanManageDeviceAdmin() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
null);
@@ -12780,27 +12782,6 @@
}
}
- private boolean hasPrinting() {
- return mInjector.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING);
- }
-
- @Override
- public void setPrintingEnabled(ComponentName admin, boolean enabled) {
- if (!mHasFeature || !hasPrinting()) {
- return;
- }
- Preconditions.checkNotNull(admin, "Admin cannot be null.");
- enforceProfileOrDeviceOwner(admin);
- synchronized (this) {
- final int userHandle = mInjector.userHandleGetCallingUserId();
- DevicePolicyData policy = getUserData(userHandle);
- if (policy.mPrintingEnabled != enabled) {
- policy.mPrintingEnabled = enabled;
- saveSettingsLocked(userHandle);
- }
- }
- }
-
private void deleteTransferOwnershipMetadataFileLocked() {
mTransferOwnershipMetadataManager.deleteMetadataFile();
}
@@ -12830,25 +12811,6 @@
}
}
- /**
- * Returns whether printing is enabled for current user.
- * @hide
- */
- @Override
- public boolean isPrintingEnabled() {
- if (!hasPrinting()) {
- return false;
- }
- if (!mHasFeature) {
- return true;
- }
- synchronized (this) {
- final int userHandle = mInjector.userHandleGetCallingUserId();
- DevicePolicyData policy = getUserData(userHandle);
- return policy.mPrintingEnabled;
- }
- }
-
@Override
public int addOverrideApn(@NonNull ComponentName who, @NonNull ApnSetting apnSetting) {
if (!mHasFeature) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 3277adf..fb34913 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -16,6 +16,9 @@
package com.android.server.devicepolicy;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
@@ -30,6 +33,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -55,6 +59,7 @@
SecurityLogMonitor(DevicePolicyManagerService service, long id) {
this.mService = service;
this.mId = id;
+ this.mLastForceNanos = System.nanoTime();
}
private static final boolean DEBUG = false; // STOPSHIP if true.
@@ -77,20 +82,22 @@
/**
* How often should Device Owner be notified under normal circumstances.
*/
- private static final long RATE_LIMIT_INTERVAL_MILLISECONDS = TimeUnit.HOURS.toMillis(2);
+ private static final long RATE_LIMIT_INTERVAL_MS = TimeUnit.HOURS.toMillis(2);
/**
* How often to retry the notification about available logs if it is ignored or missed by DO.
*/
- private static final long BROADCAST_RETRY_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(30);
+ private static final long BROADCAST_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(30);
/**
* Internally how often should the monitor poll the security logs from logd.
*/
- private static final long POLLING_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(1);
+ private static final long POLLING_INTERVAL_MS = TimeUnit.MINUTES.toMillis(1);
/**
* Overlap between two subsequent log requests, required to avoid losing out of order events.
*/
- private static final long OVERLAP_NANOS = TimeUnit.SECONDS.toNanos(3);
+ private static final long OVERLAP_NS = TimeUnit.SECONDS.toNanos(3);
+ /** Minimum time between forced fetch attempts. */
+ private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10);
@GuardedBy("mLock")
private Thread mMonitorThread = null;
@@ -122,6 +129,13 @@
@GuardedBy("mLock")
private boolean mPaused = false;
+ /** Semaphore used to force log fetching on request from adb. */
+ private final Semaphore mForceSemaphore = new Semaphore(0 /* permits */);
+
+ /** The last timestamp when force fetch was used, to prevent abuse. */
+ @GuardedBy("mForceSemaphore")
+ private long mLastForceNanos = 0;
+
void start() {
Slog.i(TAG, "Starting security logging.");
SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED);
@@ -203,7 +217,7 @@
Slog.i(TAG, "Resumed.");
try {
- notifyDeviceOwnerIfNeeded();
+ notifyDeviceOwnerIfNeeded(false /* force */);
} catch (InterruptedException e) {
Log.w(TAG, "Thread interrupted.", e);
}
@@ -231,7 +245,7 @@
if (mAllowedToRetrieve) {
mAllowedToRetrieve = false;
mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
- + RATE_LIMIT_INTERVAL_MILLISECONDS;
+ + RATE_LIMIT_INTERVAL_MS;
List<SecurityEvent> result = mPendingLogs;
mPendingLogs = new ArrayList<>();
mCriticalLevelLogged = false;
@@ -247,8 +261,7 @@
/**
* Requests the next (or the first) batch of events from the log with appropriate timestamp.
*/
- private void getNextBatch(ArrayList<SecurityEvent> newLogs)
- throws IOException, InterruptedException {
+ private void getNextBatch(ArrayList<SecurityEvent> newLogs) throws IOException {
if (mLastEventNanos < 0) {
// Non-blocking read that returns all logs immediately.
if (DEBUG) Slog.d(TAG, "SecurityLog.readEvents");
@@ -257,7 +270,7 @@
// If we have last events from the previous batch, request log events with time overlap
// with previously retrieved messages to avoid losing events due to reordering in logd.
final long startNanos = mLastEvents.isEmpty()
- ? mLastEventNanos : Math.max(0, mLastEventNanos - OVERLAP_NANOS);
+ ? mLastEventNanos : Math.max(0, mLastEventNanos - OVERLAP_NS);
if (DEBUG) Slog.d(TAG, "SecurityLog.readEventsSince: " + startNanos);
// Non-blocking read that returns all logs with timestamps >= startNanos immediately.
SecurityLog.readEventsSince(startNanos, newLogs);
@@ -293,7 +306,7 @@
// Position of the earliest event that has to be saved. Start from the penultimate event,
// going backward.
int pos = newLogs.size() - 2;
- while (pos >= 0 && mLastEventNanos - newLogs.get(pos).getTimeNanos() < OVERLAP_NANOS) {
+ while (pos >= 0 && mLastEventNanos - newLogs.get(pos).getTimeNanos() < OVERLAP_NS) {
pos--;
}
// We either run past the start of the list or encountered an event that is too old to keep.
@@ -401,10 +414,11 @@
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- ArrayList<SecurityEvent> newLogs = new ArrayList<>();
+ final ArrayList<SecurityEvent> newLogs = new ArrayList<>();
while (!Thread.currentThread().isInterrupted()) {
try {
- Thread.sleep(POLLING_INTERVAL_MILLISECONDS);
+ final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS);
+
getNextBatch(newLogs);
mLock.lockInterruptibly();
@@ -416,7 +430,7 @@
saveLastEvents(newLogs);
newLogs.clear();
- notifyDeviceOwnerIfNeeded();
+ notifyDeviceOwnerIfNeeded(force);
} catch (IOException e) {
Log.e(TAG, "Failed to read security log", e);
} catch (InterruptedException e) {
@@ -437,7 +451,7 @@
Slog.i(TAG, "MonitorThread exit.");
}
- private void notifyDeviceOwnerIfNeeded() throws InterruptedException {
+ private void notifyDeviceOwnerIfNeeded(boolean force) throws InterruptedException {
boolean allowRetrievalAndNotifyDO = false;
mLock.lockInterruptibly();
try {
@@ -445,8 +459,8 @@
return;
}
final int logSize = mPendingLogs.size();
- if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) {
- // Allow DO to retrieve logs if too many pending logs
+ if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL || (force && logSize > 0)) {
+ // Allow DO to retrieve logs if too many pending logs or if forced.
if (!mAllowedToRetrieve) {
allowRetrievalAndNotifyDO = true;
}
@@ -461,7 +475,7 @@
mAllowedToRetrieve = true;
// Set the timeout to retry the notification if the DO misses it.
mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
- + BROADCAST_RETRY_INTERVAL_MILLISECONDS;
+ + BROADCAST_RETRY_INTERVAL_MS;
}
} finally {
mLock.unlock();
@@ -472,4 +486,26 @@
null);
}
}
+
+ /**
+ * Forces the logs to be fetched and made available. Returns 0 on success or timeout to wait
+ * before attempting in milliseconds.
+ */
+ public long forceLogs() {
+ final long nowNanos = System.nanoTime();
+ // We only synchronize with another calls to this function, not with the fetching thread.
+ synchronized (mForceSemaphore) {
+ final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos;
+ if (toWaitNanos > 0) {
+ return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
+ }
+ mLastForceNanos = nowNanos;
+ // There is a race condition with the fetching thread below, but if the last permit is
+ // acquired just after we do the check, logs are forced anyway and that's what we need.
+ if (mForceSemaphore.availablePermits() == 0) {
+ mForceSemaphore.release();
+ }
+ return 0;
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 210fd47..5ea113b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -65,7 +65,6 @@
import com.android.server.audio.AudioService;
import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.camera.CameraServiceProxy;
-import com.android.server.car.CarServiceHelperService;
import com.android.server.clipboard.ClipboardService;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.coverage.CoverageService;
@@ -220,6 +219,8 @@
"com.google.android.things.services.IoTSystemService";
private static final String SLICE_MANAGER_SERVICE_CLASS =
"com.android.server.slice.SliceManagerService$Lifecycle";
+ private static final String CAR_SERVICE_HELPER_SERVICE_CLASS =
+ "com.android.internal.car.CarServiceHelperService";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -723,21 +724,8 @@
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
- boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
- boolean disableMediaProjection = SystemProperties.getBoolean("config.disable_mediaproj",
- false);
- boolean disableSerial = SystemProperties.getBoolean("config.disable_serial", false);
- boolean disableSearchManager = SystemProperties.getBoolean("config.disable_searchmanager",
- false);
- boolean disableTrustManager = SystemProperties.getBoolean("config.disable_trustmanager",
- false);
- boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices",
- false);
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
- boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false);
- boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
@@ -745,6 +733,9 @@
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+ boolean isWatch = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) {
throw new RuntimeException();
@@ -819,7 +810,7 @@
ServiceManager.addService("vibrator", vibrator);
traceEnd();
- if (!disableConsumerIr) {
+ if (!isWatch) {
traceBeginAndSlog("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);
ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -862,7 +853,7 @@
traceLog.traceEnd();
}, START_HIDL_SERVICES);
- if (!disableVrManager) {
+ if (!isWatch) {
traceBeginAndSlog("StartVrManagerService");
mSystemServiceManager.startService(VrManagerService.class);
traceEnd();
@@ -1030,7 +1021,7 @@
mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
traceEnd();
- if (!disableSystemUI) {
+ if (!isWatch) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context, wm);
@@ -1063,11 +1054,9 @@
}
traceEnd();
- if (!disableTextServices) {
- traceBeginAndSlog("StartTextServicesManager");
- mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartTextServicesManager");
+ mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
+ traceEnd();
if (!disableSystemTextClassifier) {
traceBeginAndSlog("StartTextClassificationManagerService");
@@ -1115,18 +1104,12 @@
traceEnd();
}
- if (!disableRtt) {
- traceBeginAndSlog("StartWifiRtt");
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
traceEnd();
-
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
- traceBeginAndSlog("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
- traceEnd();
- }
}
if (context.getPackageManager().hasSystemFeature(
@@ -1204,7 +1187,6 @@
SystemNotificationChannels.createAll(context);
notification = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- networkPolicy.bindNotificationManager(notification);
traceEnd();
traceBeginAndSlog("StartDeviceMonitor");
@@ -1229,7 +1211,7 @@
}
traceEnd();
- if (!disableSearchManager) {
+ if (!isWatch) {
traceBeginAndSlog("StartSearchManagerService");
try {
mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS);
@@ -1259,7 +1241,7 @@
mSystemServiceManager.startService(DockObserver.class);
traceEnd();
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (isWatch) {
traceBeginAndSlog("StartThermalObserver");
mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
traceEnd();
@@ -1291,7 +1273,7 @@
traceEnd();
}
- if (!disableSerial) {
+ if (!isWatch) {
traceBeginAndSlog("StartSerialService");
try {
// Serial port support
@@ -1331,11 +1313,9 @@
mSystemServiceManager.startService(SoundTriggerService.class);
traceEnd();
- if (!disableTrustManager) {
- traceBeginAndSlog("StartTrustManager");
- mSystemServiceManager.startService(TrustManagerService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartTrustManager");
+ mSystemServiceManager.startService(TrustManagerService.class);
+ traceEnd();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
traceBeginAndSlog("StartBackupManager");
@@ -1392,14 +1372,16 @@
traceEnd();
}
- traceBeginAndSlog("StartNetworkTimeUpdateService");
- try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- ServiceManager.addService("network_time_update_service", networkTimeUpdater);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
+ if (!isWatch) {
+ traceBeginAndSlog("StartNetworkTimeUpdateService");
+ try {
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ ServiceManager.addService("network_time_update_service", networkTimeUpdater);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
+ }
+ traceEnd();
}
- traceEnd();
traceBeginAndSlog("StartCommonTimeManagementService");
try {
@@ -1535,13 +1517,13 @@
traceEnd();
}
- if (!disableMediaProjection) {
+ if (!isWatch) {
traceBeginAndSlog("StartMediaProjectionManager");
mSystemServiceManager.startService(MediaProjectionManagerService.class);
traceEnd();
}
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (isWatch) {
traceBeginAndSlog("StartWearConnectivityService");
mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
traceEnd();
@@ -1762,7 +1744,7 @@
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
traceBeginAndSlog("StartCarServiceHelperService");
- mSystemServiceManager.startService(CarServiceHelperService.class);
+ mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
traceEnd();
}
diff --git a/services/net/OWNERS b/services/net/OWNERS
index 6f77e04..ce50558 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7d9736e..d190432 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -1068,6 +1068,7 @@
mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
}
+ @GuardedBy("this")
private void logApfProgramEventLocked(long now) {
if (mLastInstallEvent == null) {
return;
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 49a1e79..8fbc01e 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -268,6 +268,7 @@
mUnicastResponder = null;
}
+ @GuardedBy("mLock")
private void assembleRaLocked() {
final ByteBuffer ra = ByteBuffer.wrap(mRA);
ra.order(ByteOrder.BIG_ENDIAN);
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index d6cc805..e8620ed 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
-import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -61,10 +60,10 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -115,12 +114,9 @@
private final SparseArray<UserState> mUserStates = new SparseArray<>();
- private final DevicePolicyManager mDpm;
-
PrintManagerImpl(Context context) {
mContext = context;
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
registerContentObservers();
registerBroadcastReceivers();
}
@@ -708,12 +704,12 @@
final long identity = Binder.clearCallingIdentity();
try {
if (dumpAsProto) {
- dump(new DualDumpOutputStream(new ProtoOutputStream(fd), null),
+ dump(new DualDumpOutputStream(new ProtoOutputStream(fd)),
userStatesToDump);
} else {
pw.println("PRINT MANAGER STATE (dumpsys print)");
- dump(new DualDumpOutputStream(null, new IndentingPrintWriter(pw, " ")),
+ dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
userStatesToDump);
}
} finally {
@@ -722,7 +718,7 @@
}
private boolean isPrintingEnabled() {
- return mDpm == null || mDpm.isPrintingEnabled();
+ return !mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING);
}
private void dump(@NonNull DualDumpOutputStream dumpStream,
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 80b97cf..f72d8ee 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -16,8 +16,8 @@
package com.android.server.print;
-import static com.android.internal.print.DumpUtils.writeComponentName;
import static com.android.internal.print.DumpUtils.writePrinterId;
+import static com.android.internal.util.dump.DumpUtils.writeComponentName;
import android.annotation.FloatRange;
import android.annotation.NonNull;
@@ -49,7 +49,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.print.DualDumpOutputStream;
+import com.android.internal.util.dump.DualDumpOutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index a69baa1..c1c32c2 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -46,7 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.TransferPipe;
-import com.android.internal.print.DualDumpOutputStream;
+import com.android.internal.util.dump.DualDumpOutputStream;
import libcore.io.IoUtils;
@@ -596,6 +596,7 @@
}
}
+ @GuardedBy("mLock")
private void bindLocked() throws TimeoutException, InterruptedException {
while (mIsBinding) {
mLock.wait();
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index e2808e8..84c1bb2 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -20,11 +20,11 @@
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
-import static com.android.internal.print.DumpUtils.writeComponentName;
import static com.android.internal.print.DumpUtils.writePrintJobInfo;
import static com.android.internal.print.DumpUtils.writePrinterId;
import static com.android.internal.print.DumpUtils.writePrinterInfo;
-import static com.android.internal.print.DumpUtils.writeStringIfNotNull;
+import static com.android.internal.util.dump.DumpUtils.writeComponentName;
+import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
@@ -81,7 +81,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.print.DualDumpOutputStream;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
import com.android.server.print.RemotePrintServiceRecommendationService
.RemotePrintServiceRecommendationServiceCallbacks;
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 218a2b8..b3fac48 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -27,7 +27,8 @@
ShortcutManagerTestUtils \
truth-prebuilt \
testables \
- testng
+ testng \
+ platformprotosnano
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-1-no-begin-end.pem b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-1-no-begin-end.pem
new file mode 100644
index 0000000..b5d513c
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-1-no-begin-end.pem
@@ -0,0 +1,49 @@
+MIIJJzCCBQ6gAwIBAgIJAM7fBGeQ1wBkMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV
+BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAxMTEwNjQ5MzNaFw0zODAx
+MDYwNjQ5MzNaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCBCIw
+DQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBCcFv05M1seLPYIW7oFivh7u5otCt
+Mm7ryq0UjpbTQPcQxJQbYzcUQF7CbPYTdWDid4EuO8Zec03ownsduFhKud6H3yIQ
+4ueGiqCBoG1D6+N8fF9R8awTmAsbNg63VInx6IwcBZnjFlsIIftOFxqIJBYhiKhN
+JhPtF1i9dn+N5HjEKmkJO3pXhYhRXMp7OwL/epxzhBXFYT7aDg9pnaM6C+4hmhQ/
+0q2oyzYtAqFmrEenbtI6G47SzMc+shNTuYLtq21j/Z3uA3RwB9Szfu99F66tlgTX
+v7K7YS573hN3TQY/+nkLfFy/oF2LQRYvKHF+Nv0BHzQLzqDEYBaILcMf3i2Ce/b7
+wZjitLqFAI1swqGzgH/QpB3OrX51M/B7UCF2nB7Pa8knu4kBDGkz2Q41jAL0W/qt
+j43VwJDW0Y98OuqQiCqJrTrGdv7b/phnVVBvFrtIjYMfyK34jy5VLXctV5CSkWj5
+3ul3mvGFHJD+6nneDR4PUkmYN0khT4t/RqnQlwYE0a6Erq1+Rof6/DoWSzeBLBYV
+JaHhRy9mrudR/VcQynLKty6Zst4Lyh6aPMHcpTwGZbG+4mXnWeTaLEnGvivldksT
+XOxipcO/fXJfDss4b0glGzP3GD0+H5EZB9coYzNT47QZd9drxHdrLxtPoi+MeqkG
+gCdyFyBZO8G2k/JuyziT6hy+50VXJnl6Ujxj7MVUYAsISHsHgqETDsukQbbKvTKg
+3gxPVNN/vKWwyh7KLcFIaOEoPOgStkmVsqrXm7YLE6Bvzm8nu4rwJeAF9Yseg9BE
+Y86TRRmAI7fW4eDEPnxgCUUvcYSAh5mcayIyIr0KTuXkevwYbVRHMVmy9DaqzsP8
+YFXIqFvDXRCFSy/gMkoNb9ZoqdkmjZ+VBsjAKI+u/Haf6pgdpGZbVGKEFmaVHCkr
+tPp/gy4kE4qmd/SIaccG8o6Eb9X9fbqTTDZv34kcGgxOvBJVIaNHprTjgvYEnRaD
+KTlmZoCUmBlHzvbf68YWBmIz0K8vYPdx9r98LiUgpbTHtKZIYrJnbgPnbC9icP24
+2ksB4yaTx1QWc14vTNv1lUtv4zJEmaaoynNlETJFf/Tz0QKJxtT+l/BIAz8kEJMA
+cKsfoTx9OTtfuL85pXbCgxbKKmKn6RzxUCzSzgMboC0z6W8Zxy2gLIhqMm8AXAF7
+salwrRirV4lWsM9MOhVEgfjcv/qmQSYr1ARrwwegHRqxPA3qh11kfq5YSFU7W7+f
+JrWH6VuLZ0B1fj2+lsoMNekFA1ULD8DK7aAFIh9Y1y4Jt//xMuOPcD5PWNGFmUk7
+oPewiIUMLjXSWcgrQVYbZEDW/vooMJoo47Vg1fQPehejbONE1nBIaeRVhJcCAwEA
+AaNjMGEwHQYDVR0OBBYEFNd7oYeSi7hSGimRpTZaHLQy6+zRMB8GA1UdIwQYMBaA
+FNd7oYeSi7hSGimRpTZaHLQy6+zRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IEAgABny011veuPplTcNeyLeKhWRn/y9VM
+QEyhaLlsC1Di35WN8owjj4+gugLlDdtEc/pvzgk+ZkAuplQkrsU907dvwkDPb4rW
+ZB5hjbr9yVyEHK1bHh7RSUkJrB3NRwqH1lyWz/LfaVfbV4CtaaERhThzZCp/MweO
+Tivg2XpSluy5s8lEEbMC/dGuIsBMitX3XLlbYa2ND3aHZLo6X9yQMFfTCjgwYG2n
+eDYupnvcLINlrlJYcrYSrIvoQn9XfsnjU3AXz+jc6xLrO3EtXDhi9e+QTfcnvRsg
+l/Hj9SZr1w1L1PPJo+KjsRavVvzaHXlBAvvUtEojJrkR3j+b5zvQB6dgVrM0zUhM
+Q9zRp5R/xqHeZ/0TTQe9kEa8QuRzuRIkK5Wbh76Eix3S+2uTsbj462nk4E0oPR8p
+iYopS4ZEFEfrKW14HOph9ZscI4l/HfDmTNfgpyFl62UrvzVBnoz+sbhTgbPHPcCX
+OUrhmpz9I5oBkyEAZYunSvzY/9SXUsz6psXHJmVzLQcne/YQTtpWzV/wGD7cjjDl
+bfzsmGCfZ8jqPBoicl5IUVdyZsJgufEZHXxKZQ7wL7R6jKrj/GtCDey1Wr2QT8VX
+5JTk9cJQFjgjDWaAyCBpGEaQvYJcaOxk2D+Wap5ax8nUfW/99vVFA0EJKsSVVzw7
+daRty0UpfZsx2Sfzpg0mymmgB8+NY6t68dL5C/xxAv5mEQ8wGJmP45iQpo5T6LVV
+MrktLf5eIzxlALQIW/AgpSH9JKCqpItdxfisAIIs9e8XHbVJJA0Jde7rtAj+TUY0
+h00xSqyfSSbpcDJ9lIoSZOJvFQdWOxB8c3vZZGGhMuRFm06sUHvcHjo8KwnbqyOx
+DGjeqt6YWty6WcNin0WciR33vGHIzwVNxNnmuY308bNsMvY9jsmd37hdmmwnmQge
+7AIa7TMPjaKm0vV/1ztFSODWCI2K7klmL2MtOJMGfqUeOfjPANbS3lMJBAH9qxLM
+7Kng+nfqVtt+NG9MxcTbP80FkBa/6JxGgjjsiwDmhr2MTCYOK/eD+WZikMOieyvH
+m2vgxYCdWrhaGfc3t6oQ2YO+mXI7e6d3F3a90UUYkBIgje9zu0RLxnBBhuoRyGwv
+uQAlqgMDBZIzTO0Vnwew7KRLdzLhWbiikhi81q6Lg62aWjbdF6Ue6AVXch+dqmr+
+9aVt0Y6ETTS77nrQyglyLKIeNx6cEHDjETXlPYGbCAlrdKAdTA4ngnBZnzGQ/8zg
+tP9zvIJVA6cuOAn8GFEsrb7GN20QSDwyJWrYi6f+m64D9rOK4Jz4t+lEfjcfJeM/
+UcNlhmATcMHXWPCoKkOfll4PBc/Wigv1xYw70RZ4pai07LzJxNHYhvpE3Q==
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-2-empty-block.pem b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-2-empty-block.pem
new file mode 100644
index 0000000..c412709
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-2-empty-block.pem
@@ -0,0 +1,2 @@
+-----BEGIN CERTIFICATE-----
+-----END CERTIFICATE-----
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-3-invalid-key.pem b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-3-invalid-key.pem
new file mode 100644
index 0000000..9137b16
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/invalid-cert-3-invalid-key.pem
@@ -0,0 +1,50 @@
+-----BEGIN CERTIFICATE-----
+BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAxMTEwNjQ5MzNaFw0zODAx
+MDYwNjQ5MzNaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCBCIw
+DQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBCcFv05M1seLPYIW7oFivh7u5otCt
+Mm7ryq0UjpbTQPcQxJQbYzcUQF7CbPYTdWDid4EuO8Zec03ownsduFhKud6H3yIQ
+4ueGiqCBoG1D6+N8fF9R8awTmAsbNg63VInx6IwcBZnjFlsIIftOFxqIJBYhiKhN
+JhPtF1i9dn+N5HjEKmkJO3pXhYhRXMp7OwL/epxzhBXFYT7aDg9pnaM6C+4hmhQ/
+0q2oyzYtAqFmrEenbtI6G47SzMc+shNTuYLtq21j/Z3uA3RwB9Szfu99F66tlgTX
+v7K7YS573hN3TQY/+nkLfFy/oF2LQRYvKHF+Nv0BHzQLzqDEYBaILcMf3i2Ce/b7
+wZjitLqFAI1swqGzgH/QpB3OrX51M/B7UCF2nB7Pa8knu4kBDGkz2Q41jAL0W/qt
+j43VwJDW0Y98OuqQiCqJrTrGdv7b/phnVVBvFrtIjYMfyK34jy5VLXctV5CSkWj5
+3ul3mvGFHJD+6nneDR4PUkmYN0khT4t/RqnQlwYE0a6Erq1+Rof6/DoWSzeBLBYV
+JaHhRy9mrudR/VcQynLKty6Zst4Lyh6aPMHcpTwGZbG+4mXnWeTaLEnGvivldksT
+XOxipcO/fXJfDss4b0glGzP3GD0+H5EZB9coYzNT47QZd9drxHdrLxtPoi+MeqkG
+gCdyFyBZO8G2k/JuyziT6hy+50VXJnl6Ujxj7MVUYAsISHsHgqETDsukQbbKvTKg
+3gxPVNN/vKWwyh7KLcFIaOEoPOgStkmVsqrXm7YLE6Bvzm8nu4rwJeAF9Yseg9BE
+Y86TRRmAI7fW4eDEPnxgCUUvcYSAh5mcayIyIr0KTuXkevwYbVRHMVmy9DaqzsP8
+YFXIqFvDXRCFSy/gMkoNb9ZoqdkmjZ+VBsjAKI+u/Haf6pgdpGZbVGKEFmaVHCkr
+tPp/gy4kE4qmd/SIaccG8o6Eb9X9fbqTTDZv34kcGgxOvBJVIaNHprTjgvYEnRaD
+KTlmZoCUmBlHzvbf68YWBmIz0K8vYPdx9r98LiUgpbTHtKZIYrJnbgPnbC9icP24
+2ksB4yaTx1QWc14vTNv1lUtv4zJEmaaoynNlETJFf/Tz0QKJxtT+l/BIAz8kEJMA
+cKsfoTx9OTtfuL85pXbCgxbKKmKn6RzxUCzSzgMboC0z6W8Zxy2gLIhqMm8AXAF7
+salwrRirV4lWsM9MOhVEgfjcv/qmQSYr1ARrwwegHRqxPA3qh11kfq5YSFU7W7+f
+JrWH6VuLZ0B1fj2+lsoMNekFA1ULD8DK7aAFIh9Y1y4Jt//xMuOPcD5PWNGFmUk7
+oPewiIUMLjXSWcgrQVYbZEDW/vooMJoo47Vg1fQPehejbONE1nBIaeRVhJcCAwEA
+AaNjMGEwHQYDVR0OBBYEFNd7oYeSi7hSGimRpTZaHLQy6+zRMB8GA1UdIwQYMBaA
+FNd7oYeSi7hSGimRpTZaHLQy6+zRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IEAgABny011veuPplTcNeyLeKhWRn/y9VM
+QEyhaLlsC1Di35WN8owjj4+gugLlDdtEc/pvzgk+ZkAuplQkrsU907dvwkDPb4rW
+ZB5hjbr9yVyEHK1bHh7RSUkJrB3NRwqH1lyWz/LfaVfbV4CtaaERhThzZCp/MweO
+Tivg2XpSluy5s8lEEbMC/dGuIsBMitX3XLlbYa2ND3aHZLo6X9yQMFfTCjgwYG2n
+eDYupnvcLINlrlJYcrYSrIvoQn9XfsnjU3AXz+jc6xLrO3EtXDhi9e+QTfcnvRsg
+l/Hj9SZr1w1L1PPJo+KjsRavVvzaHXlBAvvUtEojJrkR3j+b5zvQB6dgVrM0zUhM
+Q9zRp5R/xqHeZ/0TTQe9kEa8QuRzuRIkK5Wbh76Eix3S+2uTsbj462nk4E0oPR8p
+iYopS4ZEFEfrKW14HOph9ZscI4l/HfDmTNfgpyFl62UrvzVBnoz+sbhTgbPHPcCX
+OUrhmpz9I5oBkyEAZYunSvzY/9SXUsz6psXHJmVzLQcne/YQTtpWzV/wGD7cjjDl
+bfzsmGCfZ8jqPBoicl5IUVdyZsJgufEZHXxKZQ7wL7R6jKrj/GtCDey1Wr2QT8VX
+5JTk9cJQFjgjDWaAyCBpGEaQvYJcaOxk2D+Wap5ax8nUfW/99vVFA0EJKsSVVzw7
+daRty0UpfZsx2Sfzpg0mymmgB8+NY6t68dL5C/xxAv5mEQ8wGJmP45iQpo5T6LVV
+MrktLf5eIzxlALQIW/AgpSH9JKCqpItdxfisAIIs9e8XHbVJJA0Jde7rtAj+TUY0
+h00xSqyfSSbpcDJ9lIoSZOJvFQdWOxB8c3vZZGGhMuRFm06sUHvcHjo8KwnbqyOx
+DGjeqt6YWty6WcNin0WciR33vGHIzwVNxNnmuY308bNsMvY9jsmd37hdmmwnmQge
+7AIa7TMPjaKm0vV/1ztFSODWCI2K7klmL2MtOJMGfqUeOfjPANbS3lMJBAH9qxLM
+7Kng+nfqVtt+NG9MxcTbP80FkBa/6JxGgjjsiwDmhr2MTCYOK/eD+WZikMOieyvH
+m2vgxYCdWrhaGfc3t6oQ2YO+mXI7e6d3F3a90UUYkBIgje9zu0RLxnBBhuoRyGwv
+uQAlqgMDBZIzTO0Vnwew7KRLdzLhWbiikhi81q6Lg62aWjbdF6Ue6AVXch+dqmr+
+9aVt0Y6ETTS77nrQyglyLKIeNx6cEHDjETXlPYGbCAlrdKAdTA4ngnBZnzGQ/8zg
+tP9zvIJVA6cuOAn8GFEsrb7GN20QSDwyJWrYi6f+m64D9rOK4Jz4t+lEfjcfJeM/
+UcNlhmATcMHXWPCoKkOfll4PBc/Wigv1xYw70RZ4pai07LzJxNHYhvpE3Q==
+-----END CERTIFICATE-----
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/valid-cert-multiple-blocks.pem b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/valid-cert-multiple-blocks.pem
new file mode 100644
index 0000000..e3abeaa
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/valid-cert-multiple-blocks.pem
@@ -0,0 +1,70 @@
+-----BEGIN CERTIFICATE-----
+MIIJJzCCBQ6gAwIBAgIJAM7fBGeQ1wBkMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV
+BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAxMTEwNjQ5MzNaFw0zODAx
+MDYwNjQ5MzNaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCBCIw
+DQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBCcFv05M1seLPYIW7oFivh7u5otCt
+Mm7ryq0UjpbTQPcQxJQbYzcUQF7CbPYTdWDid4EuO8Zec03ownsduFhKud6H3yIQ
+4ueGiqCBoG1D6+N8fF9R8awTmAsbNg63VInx6IwcBZnjFlsIIftOFxqIJBYhiKhN
+JhPtF1i9dn+N5HjEKmkJO3pXhYhRXMp7OwL/epxzhBXFYT7aDg9pnaM6C+4hmhQ/
+0q2oyzYtAqFmrEenbtI6G47SzMc+shNTuYLtq21j/Z3uA3RwB9Szfu99F66tlgTX
+v7K7YS573hN3TQY/+nkLfFy/oF2LQRYvKHF+Nv0BHzQLzqDEYBaILcMf3i2Ce/b7
+wZjitLqFAI1swqGzgH/QpB3OrX51M/B7UCF2nB7Pa8knu4kBDGkz2Q41jAL0W/qt
+j43VwJDW0Y98OuqQiCqJrTrGdv7b/phnVVBvFrtIjYMfyK34jy5VLXctV5CSkWj5
+3ul3mvGFHJD+6nneDR4PUkmYN0khT4t/RqnQlwYE0a6Erq1+Rof6/DoWSzeBLBYV
+JaHhRy9mrudR/VcQynLKty6Zst4Lyh6aPMHcpTwGZbG+4mXnWeTaLEnGvivldksT
+XOxipcO/fXJfDss4b0glGzP3GD0+H5EZB9coYzNT47QZd9drxHdrLxtPoi+MeqkG
+gCdyFyBZO8G2k/JuyziT6hy+50VXJnl6Ujxj7MVUYAsISHsHgqETDsukQbbKvTKg
+3gxPVNN/vKWwyh7KLcFIaOEoPOgStkmVsqrXm7YLE6Bvzm8nu4rwJeAF9Yseg9BE
+Y86TRRmAI7fW4eDEPnxgCUUvcYSAh5mcayIyIr0KTuXkevwYbVRHMVmy9DaqzsP8
+YFXIqFvDXRCFSy/gMkoNb9ZoqdkmjZ+VBsjAKI+u/Haf6pgdpGZbVGKEFmaVHCkr
+tPp/gy4kE4qmd/SIaccG8o6Eb9X9fbqTTDZv34kcGgxOvBJVIaNHprTjgvYEnRaD
+KTlmZoCUmBlHzvbf68YWBmIz0K8vYPdx9r98LiUgpbTHtKZIYrJnbgPnbC9icP24
+2ksB4yaTx1QWc14vTNv1lUtv4zJEmaaoynNlETJFf/Tz0QKJxtT+l/BIAz8kEJMA
+cKsfoTx9OTtfuL85pXbCgxbKKmKn6RzxUCzSzgMboC0z6W8Zxy2gLIhqMm8AXAF7
+salwrRirV4lWsM9MOhVEgfjcv/qmQSYr1ARrwwegHRqxPA3qh11kfq5YSFU7W7+f
+JrWH6VuLZ0B1fj2+lsoMNekFA1ULD8DK7aAFIh9Y1y4Jt//xMuOPcD5PWNGFmUk7
+oPewiIUMLjXSWcgrQVYbZEDW/vooMJoo47Vg1fQPehejbONE1nBIaeRVhJcCAwEA
+AaNjMGEwHQYDVR0OBBYEFNd7oYeSi7hSGimRpTZaHLQy6+zRMB8GA1UdIwQYMBaA
+FNd7oYeSi7hSGimRpTZaHLQy6+zRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IEAgABny011veuPplTcNeyLeKhWRn/y9VM
+QEyhaLlsC1Di35WN8owjj4+gugLlDdtEc/pvzgk+ZkAuplQkrsU907dvwkDPb4rW
+ZB5hjbr9yVyEHK1bHh7RSUkJrB3NRwqH1lyWz/LfaVfbV4CtaaERhThzZCp/MweO
+Tivg2XpSluy5s8lEEbMC/dGuIsBMitX3XLlbYa2ND3aHZLo6X9yQMFfTCjgwYG2n
+eDYupnvcLINlrlJYcrYSrIvoQn9XfsnjU3AXz+jc6xLrO3EtXDhi9e+QTfcnvRsg
+l/Hj9SZr1w1L1PPJo+KjsRavVvzaHXlBAvvUtEojJrkR3j+b5zvQB6dgVrM0zUhM
+Q9zRp5R/xqHeZ/0TTQe9kEa8QuRzuRIkK5Wbh76Eix3S+2uTsbj462nk4E0oPR8p
+iYopS4ZEFEfrKW14HOph9ZscI4l/HfDmTNfgpyFl62UrvzVBnoz+sbhTgbPHPcCX
+OUrhmpz9I5oBkyEAZYunSvzY/9SXUsz6psXHJmVzLQcne/YQTtpWzV/wGD7cjjDl
+bfzsmGCfZ8jqPBoicl5IUVdyZsJgufEZHXxKZQ7wL7R6jKrj/GtCDey1Wr2QT8VX
+5JTk9cJQFjgjDWaAyCBpGEaQvYJcaOxk2D+Wap5ax8nUfW/99vVFA0EJKsSVVzw7
+daRty0UpfZsx2Sfzpg0mymmgB8+NY6t68dL5C/xxAv5mEQ8wGJmP45iQpo5T6LVV
+MrktLf5eIzxlALQIW/AgpSH9JKCqpItdxfisAIIs9e8XHbVJJA0Jde7rtAj+TUY0
+h00xSqyfSSbpcDJ9lIoSZOJvFQdWOxB8c3vZZGGhMuRFm06sUHvcHjo8KwnbqyOx
+DGjeqt6YWty6WcNin0WciR33vGHIzwVNxNnmuY308bNsMvY9jsmd37hdmmwnmQge
+7AIa7TMPjaKm0vV/1ztFSODWCI2K7klmL2MtOJMGfqUeOfjPANbS3lMJBAH9qxLM
+7Kng+nfqVtt+NG9MxcTbP80FkBa/6JxGgjjsiwDmhr2MTCYOK/eD+WZikMOieyvH
+m2vgxYCdWrhaGfc3t6oQ2YO+mXI7e6d3F3a90UUYkBIgje9zu0RLxnBBhuoRyGwv
+uQAlqgMDBZIzTO0Vnwew7KRLdzLhWbiikhi81q6Lg62aWjbdF6Ue6AVXch+dqmr+
+9aVt0Y6ETTS77nrQyglyLKIeNx6cEHDjETXlPYGbCAlrdKAdTA4ngnBZnzGQ/8zg
+tP9zvIJVA6cuOAn8GFEsrb7GN20QSDwyJWrYi6f+m64D9rOK4Jz4t+lEfjcfJeM/
+UcNlhmATcMHXWPCoKkOfll4PBc/Wigv1xYw70RZ4pai07LzJxNHYhvpE3Q==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+BuwwuQxvQDF4pmQd
+-----END CERTIFICATE-----
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/valid-cert.pem b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/valid-cert.pem
new file mode 100644
index 0000000..5c16a1f
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/pem/valid-cert.pem
@@ -0,0 +1,204 @@
+-----BEGIN CERTIFICATE-----
+MIIJJzCCBQ6gAwIBAgIJAM7fBGeQ1wBkMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV
+BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAxMTEwNjQ5MzNaFw0zODAx
+MDYwNjQ5MzNaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCBCIw
+DQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBCcFv05M1seLPYIW7oFivh7u5otCt
+Mm7ryq0UjpbTQPcQxJQbYzcUQF7CbPYTdWDid4EuO8Zec03ownsduFhKud6H3yIQ
+4ueGiqCBoG1D6+N8fF9R8awTmAsbNg63VInx6IwcBZnjFlsIIftOFxqIJBYhiKhN
+JhPtF1i9dn+N5HjEKmkJO3pXhYhRXMp7OwL/epxzhBXFYT7aDg9pnaM6C+4hmhQ/
+0q2oyzYtAqFmrEenbtI6G47SzMc+shNTuYLtq21j/Z3uA3RwB9Szfu99F66tlgTX
+v7K7YS573hN3TQY/+nkLfFy/oF2LQRYvKHF+Nv0BHzQLzqDEYBaILcMf3i2Ce/b7
+wZjitLqFAI1swqGzgH/QpB3OrX51M/B7UCF2nB7Pa8knu4kBDGkz2Q41jAL0W/qt
+j43VwJDW0Y98OuqQiCqJrTrGdv7b/phnVVBvFrtIjYMfyK34jy5VLXctV5CSkWj5
+3ul3mvGFHJD+6nneDR4PUkmYN0khT4t/RqnQlwYE0a6Erq1+Rof6/DoWSzeBLBYV
+JaHhRy9mrudR/VcQynLKty6Zst4Lyh6aPMHcpTwGZbG+4mXnWeTaLEnGvivldksT
+XOxipcO/fXJfDss4b0glGzP3GD0+H5EZB9coYzNT47QZd9drxHdrLxtPoi+MeqkG
+gCdyFyBZO8G2k/JuyziT6hy+50VXJnl6Ujxj7MVUYAsISHsHgqETDsukQbbKvTKg
+3gxPVNN/vKWwyh7KLcFIaOEoPOgStkmVsqrXm7YLE6Bvzm8nu4rwJeAF9Yseg9BE
+Y86TRRmAI7fW4eDEPnxgCUUvcYSAh5mcayIyIr0KTuXkevwYbVRHMVmy9DaqzsP8
+YFXIqFvDXRCFSy/gMkoNb9ZoqdkmjZ+VBsjAKI+u/Haf6pgdpGZbVGKEFmaVHCkr
+tPp/gy4kE4qmd/SIaccG8o6Eb9X9fbqTTDZv34kcGgxOvBJVIaNHprTjgvYEnRaD
+KTlmZoCUmBlHzvbf68YWBmIz0K8vYPdx9r98LiUgpbTHtKZIYrJnbgPnbC9icP24
+2ksB4yaTx1QWc14vTNv1lUtv4zJEmaaoynNlETJFf/Tz0QKJxtT+l/BIAz8kEJMA
+cKsfoTx9OTtfuL85pXbCgxbKKmKn6RzxUCzSzgMboC0z6W8Zxy2gLIhqMm8AXAF7
+salwrRirV4lWsM9MOhVEgfjcv/qmQSYr1ARrwwegHRqxPA3qh11kfq5YSFU7W7+f
+JrWH6VuLZ0B1fj2+lsoMNekFA1ULD8DK7aAFIh9Y1y4Jt//xMuOPcD5PWNGFmUk7
+oPewiIUMLjXSWcgrQVYbZEDW/vooMJoo47Vg1fQPehejbONE1nBIaeRVhJcCAwEA
+AaNjMGEwHQYDVR0OBBYEFNd7oYeSi7hSGimRpTZaHLQy6+zRMB8GA1UdIwQYMBaA
+FNd7oYeSi7hSGimRpTZaHLQy6+zRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IEAgABny011veuPplTcNeyLeKhWRn/y9VM
+QEyhaLlsC1Di35WN8owjj4+gugLlDdtEc/pvzgk+ZkAuplQkrsU907dvwkDPb4rW
+ZB5hjbr9yVyEHK1bHh7RSUkJrB3NRwqH1lyWz/LfaVfbV4CtaaERhThzZCp/MweO
+Tivg2XpSluy5s8lEEbMC/dGuIsBMitX3XLlbYa2ND3aHZLo6X9yQMFfTCjgwYG2n
+eDYupnvcLINlrlJYcrYSrIvoQn9XfsnjU3AXz+jc6xLrO3EtXDhi9e+QTfcnvRsg
+l/Hj9SZr1w1L1PPJo+KjsRavVvzaHXlBAvvUtEojJrkR3j+b5zvQB6dgVrM0zUhM
+Q9zRp5R/xqHeZ/0TTQe9kEa8QuRzuRIkK5Wbh76Eix3S+2uTsbj462nk4E0oPR8p
+iYopS4ZEFEfrKW14HOph9ZscI4l/HfDmTNfgpyFl62UrvzVBnoz+sbhTgbPHPcCX
+OUrhmpz9I5oBkyEAZYunSvzY/9SXUsz6psXHJmVzLQcne/YQTtpWzV/wGD7cjjDl
+bfzsmGCfZ8jqPBoicl5IUVdyZsJgufEZHXxKZQ7wL7R6jKrj/GtCDey1Wr2QT8VX
+5JTk9cJQFjgjDWaAyCBpGEaQvYJcaOxk2D+Wap5ax8nUfW/99vVFA0EJKsSVVzw7
+daRty0UpfZsx2Sfzpg0mymmgB8+NY6t68dL5C/xxAv5mEQ8wGJmP45iQpo5T6LVV
+MrktLf5eIzxlALQIW/AgpSH9JKCqpItdxfisAIIs9e8XHbVJJA0Jde7rtAj+TUY0
+h00xSqyfSSbpcDJ9lIoSZOJvFQdWOxB8c3vZZGGhMuRFm06sUHvcHjo8KwnbqyOx
+DGjeqt6YWty6WcNin0WciR33vGHIzwVNxNnmuY308bNsMvY9jsmd37hdmmwnmQge
+7AIa7TMPjaKm0vV/1ztFSODWCI2K7klmL2MtOJMGfqUeOfjPANbS3lMJBAH9qxLM
+7Kng+nfqVtt+NG9MxcTbP80FkBa/6JxGgjjsiwDmhr2MTCYOK/eD+WZikMOieyvH
+m2vgxYCdWrhaGfc3t6oQ2YO+mXI7e6d3F3a90UUYkBIgje9zu0RLxnBBhuoRyGwv
+uQAlqgMDBZIzTO0Vnwew7KRLdzLhWbiikhi81q6Lg62aWjbdF6Ue6AVXch+dqmr+
+9aVt0Y6ETTS77nrQyglyLKIeNx6cEHDjETXlPYGbCAlrdKAdTA4ngnBZnzGQ/8zg
+tP9zvIJVA6cuOAn8GFEsrb7GN20QSDwyJWrYi6f+m64D9rOK4Jz4t+lEfjcfJeM/
+UcNlhmATcMHXWPCoKkOfll4PBc/Wigv1xYw70RZ4pai07LzJxNHYhvpE3Q==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ ce:df:04:67:90:d7:00:64
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: CN = Google CryptAuthVault
+ Validity
+ Not Before: Jan 11 06:49:33 2018 GMT
+ Not After : Jan 6 06:49:33 2038 GMT
+ Subject: CN = Google CryptAuthVault
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (8196 bit)
+ Modulus:
+ 09:c1:6f:d3:93:35:b1:e2:cf:60:85:bb:a0:58:af:
+ 87:bb:b9:a2:d0:ad:32:6e:eb:ca:ad:14:8e:96:d3:
+ 40:f7:10:c4:94:1b:63:37:14:40:5e:c2:6c:f6:13:
+ 75:60:e2:77:81:2e:3b:c6:5e:73:4d:e8:c2:7b:1d:
+ b8:58:4a:b9:de:87:df:22:10:e2:e7:86:8a:a0:81:
+ a0:6d:43:eb:e3:7c:7c:5f:51:f1:ac:13:98:0b:1b:
+ 36:0e:b7:54:89:f1:e8:8c:1c:05:99:e3:16:5b:08:
+ 21:fb:4e:17:1a:88:24:16:21:88:a8:4d:26:13:ed:
+ 17:58:bd:76:7f:8d:e4:78:c4:2a:69:09:3b:7a:57:
+ 85:88:51:5c:ca:7b:3b:02:ff:7a:9c:73:84:15:c5:
+ 61:3e:da:0e:0f:69:9d:a3:3a:0b:ee:21:9a:14:3f:
+ d2:ad:a8:cb:36:2d:02:a1:66:ac:47:a7:6e:d2:3a:
+ 1b:8e:d2:cc:c7:3e:b2:13:53:b9:82:ed:ab:6d:63:
+ fd:9d:ee:03:74:70:07:d4:b3:7e:ef:7d:17:ae:ad:
+ 96:04:d7:bf:b2:bb:61:2e:7b:de:13:77:4d:06:3f:
+ fa:79:0b:7c:5c:bf:a0:5d:8b:41:16:2f:28:71:7e:
+ 36:fd:01:1f:34:0b:ce:a0:c4:60:16:88:2d:c3:1f:
+ de:2d:82:7b:f6:fb:c1:98:e2:b4:ba:85:00:8d:6c:
+ c2:a1:b3:80:7f:d0:a4:1d:ce:ad:7e:75:33:f0:7b:
+ 50:21:76:9c:1e:cf:6b:c9:27:bb:89:01:0c:69:33:
+ d9:0e:35:8c:02:f4:5b:fa:ad:8f:8d:d5:c0:90:d6:
+ d1:8f:7c:3a:ea:90:88:2a:89:ad:3a:c6:76:fe:db:
+ fe:98:67:55:50:6f:16:bb:48:8d:83:1f:c8:ad:f8:
+ 8f:2e:55:2d:77:2d:57:90:92:91:68:f9:de:e9:77:
+ 9a:f1:85:1c:90:fe:ea:79:de:0d:1e:0f:52:49:98:
+ 37:49:21:4f:8b:7f:46:a9:d0:97:06:04:d1:ae:84:
+ ae:ad:7e:46:87:fa:fc:3a:16:4b:37:81:2c:16:15:
+ 25:a1:e1:47:2f:66:ae:e7:51:fd:57:10:ca:72:ca:
+ b7:2e:99:b2:de:0b:ca:1e:9a:3c:c1:dc:a5:3c:06:
+ 65:b1:be:e2:65:e7:59:e4:da:2c:49:c6:be:2b:e5:
+ 76:4b:13:5c:ec:62:a5:c3:bf:7d:72:5f:0e:cb:38:
+ 6f:48:25:1b:33:f7:18:3d:3e:1f:91:19:07:d7:28:
+ 63:33:53:e3:b4:19:77:d7:6b:c4:77:6b:2f:1b:4f:
+ a2:2f:8c:7a:a9:06:80:27:72:17:20:59:3b:c1:b6:
+ 93:f2:6e:cb:38:93:ea:1c:be:e7:45:57:26:79:7a:
+ 52:3c:63:ec:c5:54:60:0b:08:48:7b:07:82:a1:13:
+ 0e:cb:a4:41:b6:ca:bd:32:a0:de:0c:4f:54:d3:7f:
+ bc:a5:b0:ca:1e:ca:2d:c1:48:68:e1:28:3c:e8:12:
+ b6:49:95:b2:aa:d7:9b:b6:0b:13:a0:6f:ce:6f:27:
+ bb:8a:f0:25:e0:05:f5:8b:1e:83:d0:44:63:ce:93:
+ 45:19:80:23:b7:d6:e1:e0:c4:3e:7c:60:09:45:2f:
+ 71:84:80:87:99:9c:6b:22:32:22:bd:0a:4e:e5:e4:
+ 7a:fc:18:6d:54:47:31:59:b2:f4:36:aa:ce:c3:fc:
+ 60:55:c8:a8:5b:c3:5d:10:85:4b:2f:e0:32:4a:0d:
+ 6f:d6:68:a9:d9:26:8d:9f:95:06:c8:c0:28:8f:ae:
+ fc:76:9f:ea:98:1d:a4:66:5b:54:62:84:16:66:95:
+ 1c:29:2b:b4:fa:7f:83:2e:24:13:8a:a6:77:f4:88:
+ 69:c7:06:f2:8e:84:6f:d5:fd:7d:ba:93:4c:36:6f:
+ df:89:1c:1a:0c:4e:bc:12:55:21:a3:47:a6:b4:e3:
+ 82:f6:04:9d:16:83:29:39:66:66:80:94:98:19:47:
+ ce:f6:df:eb:c6:16:06:62:33:d0:af:2f:60:f7:71:
+ f6:bf:7c:2e:25:20:a5:b4:c7:b4:a6:48:62:b2:67:
+ 6e:03:e7:6c:2f:62:70:fd:b8:da:4b:01:e3:26:93:
+ c7:54:16:73:5e:2f:4c:db:f5:95:4b:6f:e3:32:44:
+ 99:a6:a8:ca:73:65:11:32:45:7f:f4:f3:d1:02:89:
+ c6:d4:fe:97:f0:48:03:3f:24:10:93:00:70:ab:1f:
+ a1:3c:7d:39:3b:5f:b8:bf:39:a5:76:c2:83:16:ca:
+ 2a:62:a7:e9:1c:f1:50:2c:d2:ce:03:1b:a0:2d:33:
+ e9:6f:19:c7:2d:a0:2c:88:6a:32:6f:00:5c:01:7b:
+ b1:a9:70:ad:18:ab:57:89:56:b0:cf:4c:3a:15:44:
+ 81:f8:dc:bf:fa:a6:41:26:2b:d4:04:6b:c3:07:a0:
+ 1d:1a:b1:3c:0d:ea:87:5d:64:7e:ae:58:48:55:3b:
+ 5b:bf:9f:26:b5:87:e9:5b:8b:67:40:75:7e:3d:be:
+ 96:ca:0c:35:e9:05:03:55:0b:0f:c0:ca:ed:a0:05:
+ 22:1f:58:d7:2e:09:b7:ff:f1:32:e3:8f:70:3e:4f:
+ 58:d1:85:99:49:3b:a0:f7:b0:88:85:0c:2e:35:d2:
+ 59:c8:2b:41:56:1b:64:40:d6:fe:fa:28:30:9a:28:
+ e3:b5:60:d5:f4:0f:7a:17:a3:6c:e3:44:d6:70:48:
+ 69:e4:55:84:97
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ D7:7B:A1:87:92:8B:B8:52:1A:29:91:A5:36:5A:1C:B4:32:EB:EC:D1
+ X509v3 Authority Key Identifier:
+ keyid:D7:7B:A1:87:92:8B:B8:52:1A:29:91:A5:36:5A:1C:B4:32:EB:EC:D1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ Signature Algorithm: sha512WithRSAEncryption
+ 01:9f:2d:35:d6:f7:ae:3e:99:53:70:d7:b2:2d:e2:a1:59:19:
+ ff:cb:d5:4c:40:4c:a1:68:b9:6c:0b:50:e2:df:95:8d:f2:8c:
+ 23:8f:8f:a0:ba:02:e5:0d:db:44:73:fa:6f:ce:09:3e:66:40:
+ 2e:a6:54:24:ae:c5:3d:d3:b7:6f:c2:40:cf:6f:8a:d6:64:1e:
+ 61:8d:ba:fd:c9:5c:84:1c:ad:5b:1e:1e:d1:49:49:09:ac:1d:
+ cd:47:0a:87:d6:5c:96:cf:f2:df:69:57:db:57:80:ad:69:a1:
+ 11:85:38:73:64:2a:7f:33:07:8e:4e:2b:e0:d9:7a:52:96:ec:
+ b9:b3:c9:44:11:b3:02:fd:d1:ae:22:c0:4c:8a:d5:f7:5c:b9:
+ 5b:61:ad:8d:0f:76:87:64:ba:3a:5f:dc:90:30:57:d3:0a:38:
+ 30:60:6d:a7:78:36:2e:a6:7b:dc:2c:83:65:ae:52:58:72:b6:
+ 12:ac:8b:e8:42:7f:57:7e:c9:e3:53:70:17:cf:e8:dc:eb:12:
+ eb:3b:71:2d:5c:38:62:f5:ef:90:4d:f7:27:bd:1b:20:97:f1:
+ e3:f5:26:6b:d7:0d:4b:d4:f3:c9:a3:e2:a3:b1:16:af:56:fc:
+ da:1d:79:41:02:fb:d4:b4:4a:23:26:b9:11:de:3f:9b:e7:3b:
+ d0:07:a7:60:56:b3:34:cd:48:4c:43:dc:d1:a7:94:7f:c6:a1:
+ de:67:fd:13:4d:07:bd:90:46:bc:42:e4:73:b9:12:24:2b:95:
+ 9b:87:be:84:8b:1d:d2:fb:6b:93:b1:b8:f8:eb:69:e4:e0:4d:
+ 28:3d:1f:29:89:8a:29:4b:86:44:14:47:eb:29:6d:78:1c:ea:
+ 61:f5:9b:1c:23:89:7f:1d:f0:e6:4c:d7:e0:a7:21:65:eb:65:
+ 2b:bf:35:41:9e:8c:fe:b1:b8:53:81:b3:c7:3d:c0:97:39:4a:
+ e1:9a:9c:fd:23:9a:01:93:21:00:65:8b:a7:4a:fc:d8:ff:d4:
+ 97:52:cc:fa:a6:c5:c7:26:65:73:2d:07:27:7b:f6:10:4e:da:
+ 56:cd:5f:f0:18:3e:dc:8e:30:e5:6d:fc:ec:98:60:9f:67:c8:
+ ea:3c:1a:22:72:5e:48:51:57:72:66:c2:60:b9:f1:19:1d:7c:
+ 4a:65:0e:f0:2f:b4:7a:8c:aa:e3:fc:6b:42:0d:ec:b5:5a:bd:
+ 90:4f:c5:57:e4:94:e4:f5:c2:50:16:38:23:0d:66:80:c8:20:
+ 69:18:46:90:bd:82:5c:68:ec:64:d8:3f:96:6a:9e:5a:c7:c9:
+ d4:7d:6f:fd:f6:f5:45:03:41:09:2a:c4:95:57:3c:3b:75:a4:
+ 6d:cb:45:29:7d:9b:31:d9:27:f3:a6:0d:26:ca:69:a0:07:cf:
+ 8d:63:ab:7a:f1:d2:f9:0b:fc:71:02:fe:66:11:0f:30:18:99:
+ 8f:e3:98:90:a6:8e:53:e8:b5:55:32:b9:2d:2d:fe:5e:23:3c:
+ 65:00:b4:08:5b:f0:20:a5:21:fd:24:a0:aa:a4:8b:5d:c5:f8:
+ ac:00:82:2c:f5:ef:17:1d:b5:49:24:0d:09:75:ee:eb:b4:08:
+ fe:4d:46:34:87:4d:31:4a:ac:9f:49:26:e9:70:32:7d:94:8a:
+ 12:64:e2:6f:15:07:56:3b:10:7c:73:7b:d9:64:61:a1:32:e4:
+ 45:9b:4e:ac:50:7b:dc:1e:3a:3c:2b:09:db:ab:23:b1:0c:68:
+ de:aa:de:98:5a:dc:ba:59:c3:62:9f:45:9c:89:1d:f7:bc:61:
+ c8:cf:05:4d:c4:d9:e6:b9:8d:f4:f1:b3:6c:32:f6:3d:8e:c9:
+ 9d:df:b8:5d:9a:6c:27:99:08:1e:ec:02:1a:ed:33:0f:8d:a2:
+ a6:d2:f5:7f:d7:3b:45:48:e0:d6:08:8d:8a:ee:49:66:2f:63:
+ 2d:38:93:06:7e:a5:1e:39:f8:cf:00:d6:d2:de:53:09:04:01:
+ fd:ab:12:cc:ec:a9:e0:fa:77:ea:56:db:7e:34:6f:4c:c5:c4:
+ db:3f:cd:05:90:16:bf:e8:9c:46:82:38:ec:8b:00:e6:86:bd:
+ 8c:4c:26:0e:2b:f7:83:f9:66:62:90:c3:a2:7b:2b:c7:9b:6b:
+ e0:c5:80:9d:5a:b8:5a:19:f7:37:b7:aa:10:d9:83:be:99:72:
+ 3b:7b:a7:77:17:76:bd:d1:45:18:90:12:20:8d:ef:73:bb:44:
+ 4b:c6:70:41:86:ea:11:c8:6c:2f:b9:00:25:aa:03:03:05:92:
+ 33:4c:ed:15:9f:07:b0:ec:a4:4b:77:32:e1:59:b8:a2:92:18:
+ bc:d6:ae:8b:83:ad:9a:5a:36:dd:17:a5:1e:e8:05:57:72:1f:
+ 9d:aa:6a:fe:f5:a5:6d:d1:8e:84:4d:34:bb:ee:7a:d0:ca:09:
+ 72:2c:a2:1e:37:1e:9c:10:70:e3:11:35:e5:3d:81:9b:08:09:
+ 6b:74:a0:1d:4c:0e:27:82:70:59:9f:31:90:ff:cc:e0:b4:ff:
+ 73:bc:82:55:03:a7:2e:38:09:fc:18:51:2c:ad:be:c6:37:6d:
+ 10:48:3c:32:25:6a:d8:8b:a7:fe:9b:ae:03:f6:b3:8a:e0:9c:
+ f8:b7:e9:44:7e:37:1f:25:e3:3f:51:c3:65:86:60:13:70:c1:
+ d7:58:f0:a8:2a:43:9f:96:5e:0f:05:cf:d6:8a:0b:f5:c5:8c:
+ 3b:d1:16:78:a5:a8:b4:ec:bc:c9:c4:d1:d8:86:fa:44:dd
\ No newline at end of file
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-endpoint-cert.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-endpoint-cert.xml
new file mode 100644
index 0000000..6519f49
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-endpoint-cert.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <serial>
+ 1000
+ </serial>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <endpoints>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml
new file mode 100644
index 0000000..3da0122
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <serial>
+ 1000
+ </serial>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <endpoints>
+ <cert>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </cert>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-serial.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-serial.xml
new file mode 100644
index 0000000..4370ff0
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-serial.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <endpoints>
+ <cert>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </cert>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml
new file mode 100644
index 0000000..0f4e8a3
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <serial>
+ 1000
+ </serial>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <endpoints>
+ <cert>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </cert>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-serials.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-serials.xml
new file mode 100644
index 0000000..a2685aa
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-serials.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <serial>
+ 1000
+ </serial>
+ <serial>
+ 1000
+ </serial>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <endpoints>
+ <cert>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </cert>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-no-signature.sig.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-no-signature.sig.xml
new file mode 100644
index 0000000..5dc8ffa
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-no-signature.sig.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<signature>
+ <certificate>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </certificate>
+</signature>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-no-signer-cert.sig.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-no-signer-cert.sig.xml
new file mode 100644
index 0000000..aa81295
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-no-signer-cert.sig.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<signature>
+ <value>
+ VEVTVA==
+ </value>
+</signature>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-two-signatures.sig.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-two-signatures.sig.xml
new file mode 100644
index 0000000..09d0f44
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-two-signatures.sig.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<signature>
+ <certificate>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </certificate>
+ <value>
+ VEVTVA==
+ </value>
+ <value>
+ VEVTVA==
+ </value>
+</signature>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-two-signer-certs.sig.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-two-signer-certs.sig.xml
new file mode 100644
index 0000000..44e8993
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-sig-file-two-signer-certs.sig.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<signature>
+ <certificate>
+ signer certificate 1
+ </certificate>
+ <certificate>
+ signer certificate 2
+ </certificate>
+ <value>
+ VEVTVA==
+ </value>
+</signature>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-intermediates.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-intermediates.xml
new file mode 100644
index 0000000..e59bf36
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-intermediates.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <serial>
+ 1000
+ </serial>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <endpoints>
+ <cert>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </cert>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file.xml
new file mode 100644
index 0000000..be4893f
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<certificates>
+ <metadata>
+ <serial>
+ 1000
+ </serial>
+ <creation-time>
+ 1515697631
+ </creation-time>
+ <refresh-interval>
+ 2592000
+ </refresh-interval>
+ <previous>
+ <serial>
+ 0
+ </serial>
+ <hash>
+ 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
+ </hash>
+ </previous>
+ </metadata>
+ <intermediates>
+ <cert>
+ MIIHMDCCAxegAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVR29v
+ Z2xlIENyeXB0QXV0aFZhdWx0MB4XDTE4MDExMjAwMjM1N1oXDTI4MDExMDAwMjM1
+ N1owLTErMCkGA1UEAwwiR29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0
+ ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALlhoKYuwxaatY9ERDFp
+ iEygSnjy0xzaiF4uCyiTfAuboSi5QwGon3ohf0ufJF02L9lnTMoeBAg+88m8AMgW
+ KFcEupabqlZfA3F/50mMCmnJvBSLXJ+chUdcAVpwcZAsq6ko22ARBxao1wu2qxNe
+ D8eXiiK8DRpTtKy3wOldZJ222v35v9JGOTjORZRrcv7Z8f6I5/cSsTS+WoVk/aBt
+ QqyFkcdw1zqulnlFE2rxNAVgyLlW71WJikYTDtUDeo79LvkDGjVsLo2MfpNxuK+5
+ MuMqzyN5LmzXJmNCEW1O5IIIUAPhgy5s08G+3G644wCEWsAnv2FBWLBn/HmJu6Uq
+ nSM2AaJN56V0tJG/yL2VgTnPJrJypNTKZW3OTCLCaYcTEbKfarxLwVWxvIWSIgkn
+ 0q57GYhf7O+x9vvcOUmZwVxZECorIiK4n5AWG/KD3dWI3UGGGpYsDazRngA/bQPu
+ DSzBP9FBVcQt3/DMBG1s6f2Eko5f6aTFcVW9iV7aWLeIq+pQYlbmG42decj+aHLQ
+ COp5KV+Q77y4kFhZQFAQ1mN4crnhuEc1K5SmjAK24zIqWbwM3ly0KSQFc9jAmONg
+ 0xu7kAObP3PZk85En12yLLscNmHCWYfOOEvTHf4KX7tjBl4HHp/ur+2Qwgpt9MFB
+ MGqR2cni5OV6gZcRdHaEerjrAgMBAAGjZjBkMB0GA1UdDgQWBBRE9RxHT7U3EP1v
+ djRzNYMrU7EseDAfBgNVHSMEGDAWgBTXe6GHkou4UhopkaU2Why0Muvs0TASBgNV
+ HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
+ BAIAAfa7FKkfh4t+G8Sv4n3gUcuEAtmpCsTtHJU67r5eSvR24fDX7sqqSIib9sQ8
+ adA1FtnE3wnC4bEKnIwQLerWTN4i4V9oEKRIT5o5Rr61LyO9R+Prpo6uJVoPujFH
+ GZPdIj/Qs4Xax4a11QD+70+ZmmdL6yfigVDWA4yduFdMg6heHRf3EFCbVBv5qbBS
+ n8+eFKRnBZ/kQdFlYt+G+92Qqyn2uAcER6kZjIfPdnZh44SazLP37W8AkDX30Pmk
+ V0PHVGCDrap44q3maI1m8NAE1jGwwmRzJL953x5XgbVGt0K/3cNoWtKLenwX/G3I
+ akrgvOY5Zl0v3FRDZwGFt9UIBfZDDOGRMXIgIGs/1cvkwWpOT6dyReqDXempiQ1q
+ Yy6J5VsK5WK6gEelUyoACbzgby25V6a79Q1MI7dXmFQfCcX0nAD/AZmM1HkeYgrC
+ uq6fWoPOVMKML2mN90rCzkWxGaLcl5dPfad0O1LrcP48SRE5MXMWyxZZBon+wDIk
+ ascyM/r4fmk4kq64YKdm2wxCDMNArAIcyBkwOaWWfabtSagxJ3qtMtxK0qBUsbLC
+ yMyYpgU1h9c8rEdc4JgeE2LXJzxTKDc3SBOqbuNMlKWjYA+X+SUvVYALrQKAC+5v
+ wdUhLYdAPAksqk/ZoiBjkW35FfvqQMJBY29VnDT1h7/Nxk5gu+goTA9oFIYNrNte
+ +s0my+IUgYhKJBsgh7Mupv+B92GN5b3b440BMHB5QR959Jdq6BAXNUyZLM5fhZQE
+ Jj/rxZFXaqq757kgUhwWBz5TDbYF7GkqTyM4k430xwJKY0AYYEHmv1UYNo5X4G3x
+ SC2LhWC1b9VAykdkHbLs+IA8klxURmLmRiRj1UryhQjjT8h/FvNyPnbT1AKoElix
+ QLnLi8thkJ+tQggO0hISFsIrKNfnn0V6O0VKw9UZsMigsbYG5EbzIXcAyy8Avr9n
+ um7gBBZDt7fWso0+pG1UenJ+PybeuW/azQDLRw1Syz8OwU+ABRLq0JyyAtV7VPY5
+ C9pkKS+bU8nECxr6dMhAbpLBHlKsyb1qtkOt1p7WagEQZFIIc6svc73+L/ET/lWn
+ GBmkVVsCN7Aqyo5aXQWueXP4FUL+6O5+JALqw3qPeQgfnLkh0cUuccNND05QeEiv
+ Zswc/23KJXy1XbdVKT3UP0RAF7DxstbRGQbAT3z+n931e3KhtU28OKjsFtoeq2Dj
+ 6STPEXh4rYFWMM8+DrJetAtBqk/i+vBwRA8f7jqIPPep/vEjPqqMOpdSVcoFQ1df
+ JuOZtGfEUjFHnlDr3eGP7KUIEZvhan1zm544dDgPVTXxrY4moi2BhKEY69zRSX6B
+ +a0fa5B3pxc8BN0LsHA0stT/Y2o=
+ </cert>
+ <cert>
+ MIIESTCCAjGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwiR29v
+ Z2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTIwMDM4MDNa
+ Fw0yMzAxMTEwMDM4MDNaMDoxODA2BgNVBAMML0dvb2dsZSBDcnlwdEF1dGhWYXVs
+ dCBJbnRlcm1lZGlhdGUgSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOC
+ AQ8AMIIBCgKCAQEA0v3bN3MwKifDAkF1524XzuaxYtk1sQKUlAlNngh+Qv4RjCkX
+ TECi7po8LeNsY+hWxmW3XZ22RBphe/yP4YcOdbqlIjZYNx3b75hCSJCadOkdW+Z9
+ f6+tKsHgeUja6r9r2TThzileImAvjXShe7GZYW8csPv6HaEVRXQlu8fGAZf8skmJ
+ EMfJx84//WeULdVz94beDhi9YAf4gLfmOayQcdWhDcMYI39knJcRny1ffRGgb1Hf
+ lE+3/a3aGFeODaxfkPaGRxEhzhZ/JDBiNgUAH/u7C5nxqa2WOu5e0wq3S0TndIOE
+ hmnwCE2GvxADFQst+rSsOn4EHit70hv4CfrMRQIDAQABo2YwZDAdBgNVHQ4EFgQU
+ 0dKv4xTaEmdwHyox3tY8H8XVDGIwHwYDVR0jBBgwFoAURPUcR0+1NxD9b3Y0czWD
+ K1OxLHgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
+ hvcNAQELBQADggIBAJaArqLlJ2SLQ8JRwanD6LPlqQxucQ+x/LztTQpzPrsFfJds
+ E/sDZr2rXhDpz/bifIdj/DCbQ6/3w+t5JdFjT8GjXLgz1kCa/4W409FgSTgy1ENn
+ AMUU6pFIbOq/Qy/71uOTrrFWYTN5Vk+RBGxx5iDfHjDYraudi4VlcNkal4OyM98n
+ N3qp9cZD0RtWxMhvq6ahgmf9cTbEw6+l8yf/bogGLBYXXYeOoO5Q134AxrrgfthE
+ tvyKwJkT/l3OFKRcaHrebs+V1z5gPs7zWOyO5n2Z1SAmcOGfTfKMZWwp3Hi3OTr2
+ gB3LUYKyQVhC70dka3X+IbnFg5YfzJtX6YGnHlnI1SufOkEpGQDfcc0UQAWg/lgb
+ RkfMFD9tuJomBhyqv1YaxLN8yL4ZTRU0KCvvC5I5+X/zt9kBjnHlBOdYtknZT5jz
+ 7+mjqWdpmWoAjeV5+CgIzG2k7JAm6rQuE1ZQNF0wAYxPret4NHPJFqfD5gGhdrYw
+ pEUxkcwHERA/E1CkpyqUy/Hd3kqHvnEDqzFcxBdUdmOgnbpI2nAZdEpfxmA5+M1n
+ UoxQ8ZWAZH+Mdlkw/Hx5hVjGjz8snD4QN25pj/wT+V6AR5OmYb8yfsQb2S8a8yDp
+ HzcIHW+dEWpX2boirOsrdI16kNtxYqtG7c5qWBPJy5Zjkvh9qbnfT/RQx10g
+ </cert>
+ </intermediates>
+ <endpoints>
+ <cert>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </cert>
+ <cert>
+ MIICrDCCAZSgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwOjE4MDYGA1UEAwwvR29v
+ Z2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZSBJbnRlcm1lZGlhdGUwHhcN
+ MTgwMTEyMDEwMzA5WhcNMTkwMTEyMDEwMzA5WjArMSkwJwYDVQQDDCBHb29nbGUg
+ Q3J5cHRBdXRoVmF1bHQgSW5zdGFuY2UgMjBZMBMGByqGSM49AgEGCCqGSM49AwEH
+ A0IABGhmBQyWdjsXKJRbkW4iIrvt6iqhX5t2XGt/vZS9CoOl0fs+EvJXo4kgrnx8
+ /8SGxz3pwRkFhY943QYy6a1gv/2jgZUwgZIwCQYDVR0TBAIwADAdBgNVHQ4EFgQU
+ xFmLyxUS2JHKURBtewBKRP6kQBgwVgYDVR0jBE8wTYAU0dKv4xTaEmdwHyox3tY8
+ H8XVDGKhMaQvMC0xKzApBgNVBAMMIkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnRl
+ cm1lZGlhdGWCAhAAMA4GA1UdDwEB/wQEAwIDCDANBgkqhkiG9w0BAQsFAAOCAQEA
+ EJWpl7HU6LxukLqhw2tVZr7IRrKIucRk+RKaaiMx1Hx2jsTTskiJRiZas/xoPSqX
+ z1K5DVgI486i7HyqnWkGH5xVzCsv+rya5FOSTS3jVtgtoA4HFEqeeAcDowPDqVw3
+ yFTA55ukZnzVaPLpDfPqkhzWiuLQ/4fI6YCmOnWB8KtHTMdyGsDSAkpoxpok++NJ
+ Lu79BoBLe2ucjN383lTlieLxmrmHjF9ryYSQczcm0v6irMOMxEovw5iT4LHiEhbm
+ DfOPW909fe/s+K3TGZ3Q6U77x8g5k9dVovMgA4pFwtREtknFjeK1wXR3/eXGcP3W
+ 0bMX1yTWYJQFWCG3DFoC5w==
+ </cert>
+ <cert>
+ MIIDkjCCAXqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwiR29v
+ Z2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTIwMDU2MjNa
+ Fw0yMDA3MTMwMDU2MjNaMCsxKTAnBgNVBAMMIEdvb2dsZSBDcnlwdEF1dGhWYXVs
+ dCBJbnN0YW5jZSAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEimGC4KEks4JL
+ 83DNAAGCA5sKqqxqUPgV8gxgsijlMPL0TOtyJhJ2vSFIULEa0coVFbu+fAdxt3CV
+ DbzD0vWAmaOBiDCBhTAJBgNVHRMEAjAAMB0GA1UdDgQWBBTVeDueShnq1LCqkCFL
+ MAYtpxkCuDBJBgNVHSMEQjBAgBRE9RxHT7U3EP1vdjRzNYMrU7EseKEkpCIwIDEe
+ MBwGA1UEAwwVR29vZ2xlIENyeXB0QXV0aFZhdWx0ggIQATAOBgNVHQ8BAf8EBAMC
+ AwgwDQYJKoZIhvcNAQELBQADggIBADUZCjfBwBL9SSZDkMwE3n08schTBAgZCCrv
+ XOQVPGrfbFUcMv1mT4e8a5zxE98HsCS6K4HB40RtTXkmt6nuN+NyBrAmZJrBCqvG
+ IYtGsBLLEnojgWuSpQIBQeQy9it3RFdSR/1FIPssrWUB5KrtRvd+07+Mo7tI91jE
+ EunOrocu46g6p/OKSIZ7UmwZzczn2CJsrxuNPgqdlza3ytb+TTm536ZHnqaefSZD
+ rrruNneTXoqjC2OFn/OVLHQ1ee/vrHiX70P8p1V09cccDiwMCIZskNACYgWRdLYU
+ F5aYGueoFajrb4zmMoy8DK/4lh1EsfWMsrsQK6whmPidzgz37nb3rPpiHTdxu1Fc
+ 2XM9QV3Jfj2U5FMOZTcDha7W7kb++gSnQEPTM0+Zu6lTJmcZgK4RJ7lmIfK/eVbJ
+ 6V/wplOzXSxO3jBb2LNhLbhkUzcg68EIEPxaBpXYVOU2tSo2FMgi7/YLwQLorc6Q
+ h1pUZep8T8SLpvI02GvsB8TroFr2tCvCe5A1VxBQDx9IE7nEd2N30XxqReFk8Y82
+ xZMUOA4DL33NI45KWjhcawm0tzAPFfKjta/zYvnf7rwwE4r2PVuOXdet5eN8zBje
+ yJbEpjCemADujcwtYQ8hScyj/eCT2KNbZ9ibY2yrZEEuRQanq8CLAbpvSZYIHVhg
+ Clar7+38
+ </cert>
+ </endpoints>
+</certificates>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-sig-file-no-intermediates.sig.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-sig-file-no-intermediates.sig.xml
new file mode 100644
index 0000000..9c0adcd
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-sig-file-no-intermediates.sig.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<signature>
+ <certificate>
+ MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi
+ R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1
+ NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW
+ YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu
+ tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl
+ 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21
+ tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu
+ HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr
+ GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb
+ UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe
+ 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5
+ 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ
+ hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa
+ 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4
+ M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf
+ JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp
+ BuwwuQxvQDF4pmQd
+ </certificate>
+ <value>
+ VEVTVA==
+ </value>
+</signature>
diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-sig-file.sig.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-sig-file.sig.xml
new file mode 100644
index 0000000..f94b109
--- /dev/null
+++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-sig-file.sig.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<signature>
+ <intermediates>
+ <cert>
+ MIIHMDCCAxegAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVR29v
+ Z2xlIENyeXB0QXV0aFZhdWx0MB4XDTE4MDExMjAwMjM1N1oXDTI4MDExMDAwMjM1
+ N1owLTErMCkGA1UEAwwiR29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0
+ ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALlhoKYuwxaatY9ERDFp
+ iEygSnjy0xzaiF4uCyiTfAuboSi5QwGon3ohf0ufJF02L9lnTMoeBAg+88m8AMgW
+ KFcEupabqlZfA3F/50mMCmnJvBSLXJ+chUdcAVpwcZAsq6ko22ARBxao1wu2qxNe
+ D8eXiiK8DRpTtKy3wOldZJ222v35v9JGOTjORZRrcv7Z8f6I5/cSsTS+WoVk/aBt
+ QqyFkcdw1zqulnlFE2rxNAVgyLlW71WJikYTDtUDeo79LvkDGjVsLo2MfpNxuK+5
+ MuMqzyN5LmzXJmNCEW1O5IIIUAPhgy5s08G+3G644wCEWsAnv2FBWLBn/HmJu6Uq
+ nSM2AaJN56V0tJG/yL2VgTnPJrJypNTKZW3OTCLCaYcTEbKfarxLwVWxvIWSIgkn
+ 0q57GYhf7O+x9vvcOUmZwVxZECorIiK4n5AWG/KD3dWI3UGGGpYsDazRngA/bQPu
+ DSzBP9FBVcQt3/DMBG1s6f2Eko5f6aTFcVW9iV7aWLeIq+pQYlbmG42decj+aHLQ
+ COp5KV+Q77y4kFhZQFAQ1mN4crnhuEc1K5SmjAK24zIqWbwM3ly0KSQFc9jAmONg
+ 0xu7kAObP3PZk85En12yLLscNmHCWYfOOEvTHf4KX7tjBl4HHp/ur+2Qwgpt9MFB
+ MGqR2cni5OV6gZcRdHaEerjrAgMBAAGjZjBkMB0GA1UdDgQWBBRE9RxHT7U3EP1v
+ djRzNYMrU7EseDAfBgNVHSMEGDAWgBTXe6GHkou4UhopkaU2Why0Muvs0TASBgNV
+ HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
+ BAIAAfa7FKkfh4t+G8Sv4n3gUcuEAtmpCsTtHJU67r5eSvR24fDX7sqqSIib9sQ8
+ adA1FtnE3wnC4bEKnIwQLerWTN4i4V9oEKRIT5o5Rr61LyO9R+Prpo6uJVoPujFH
+ GZPdIj/Qs4Xax4a11QD+70+ZmmdL6yfigVDWA4yduFdMg6heHRf3EFCbVBv5qbBS
+ n8+eFKRnBZ/kQdFlYt+G+92Qqyn2uAcER6kZjIfPdnZh44SazLP37W8AkDX30Pmk
+ V0PHVGCDrap44q3maI1m8NAE1jGwwmRzJL953x5XgbVGt0K/3cNoWtKLenwX/G3I
+ akrgvOY5Zl0v3FRDZwGFt9UIBfZDDOGRMXIgIGs/1cvkwWpOT6dyReqDXempiQ1q
+ Yy6J5VsK5WK6gEelUyoACbzgby25V6a79Q1MI7dXmFQfCcX0nAD/AZmM1HkeYgrC
+ uq6fWoPOVMKML2mN90rCzkWxGaLcl5dPfad0O1LrcP48SRE5MXMWyxZZBon+wDIk
+ ascyM/r4fmk4kq64YKdm2wxCDMNArAIcyBkwOaWWfabtSagxJ3qtMtxK0qBUsbLC
+ yMyYpgU1h9c8rEdc4JgeE2LXJzxTKDc3SBOqbuNMlKWjYA+X+SUvVYALrQKAC+5v
+ wdUhLYdAPAksqk/ZoiBjkW35FfvqQMJBY29VnDT1h7/Nxk5gu+goTA9oFIYNrNte
+ +s0my+IUgYhKJBsgh7Mupv+B92GN5b3b440BMHB5QR959Jdq6BAXNUyZLM5fhZQE
+ Jj/rxZFXaqq757kgUhwWBz5TDbYF7GkqTyM4k430xwJKY0AYYEHmv1UYNo5X4G3x
+ SC2LhWC1b9VAykdkHbLs+IA8klxURmLmRiRj1UryhQjjT8h/FvNyPnbT1AKoElix
+ QLnLi8thkJ+tQggO0hISFsIrKNfnn0V6O0VKw9UZsMigsbYG5EbzIXcAyy8Avr9n
+ um7gBBZDt7fWso0+pG1UenJ+PybeuW/azQDLRw1Syz8OwU+ABRLq0JyyAtV7VPY5
+ C9pkKS+bU8nECxr6dMhAbpLBHlKsyb1qtkOt1p7WagEQZFIIc6svc73+L/ET/lWn
+ GBmkVVsCN7Aqyo5aXQWueXP4FUL+6O5+JALqw3qPeQgfnLkh0cUuccNND05QeEiv
+ Zswc/23KJXy1XbdVKT3UP0RAF7DxstbRGQbAT3z+n931e3KhtU28OKjsFtoeq2Dj
+ 6STPEXh4rYFWMM8+DrJetAtBqk/i+vBwRA8f7jqIPPep/vEjPqqMOpdSVcoFQ1df
+ JuOZtGfEUjFHnlDr3eGP7KUIEZvhan1zm544dDgPVTXxrY4moi2BhKEY69zRSX6B
+ +a0fa5B3pxc8BN0LsHA0stT/Y2o=
+ </cert>
+ </intermediates>
+ <certificate>
+ MIIESTCCAjGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwiR29v
+ Z2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTIwMDM4MDNa
+ Fw0yMzAxMTEwMDM4MDNaMDoxODA2BgNVBAMML0dvb2dsZSBDcnlwdEF1dGhWYXVs
+ dCBJbnRlcm1lZGlhdGUgSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOC
+ AQ8AMIIBCgKCAQEA0v3bN3MwKifDAkF1524XzuaxYtk1sQKUlAlNngh+Qv4RjCkX
+ TECi7po8LeNsY+hWxmW3XZ22RBphe/yP4YcOdbqlIjZYNx3b75hCSJCadOkdW+Z9
+ f6+tKsHgeUja6r9r2TThzileImAvjXShe7GZYW8csPv6HaEVRXQlu8fGAZf8skmJ
+ EMfJx84//WeULdVz94beDhi9YAf4gLfmOayQcdWhDcMYI39knJcRny1ffRGgb1Hf
+ lE+3/a3aGFeODaxfkPaGRxEhzhZ/JDBiNgUAH/u7C5nxqa2WOu5e0wq3S0TndIOE
+ hmnwCE2GvxADFQst+rSsOn4EHit70hv4CfrMRQIDAQABo2YwZDAdBgNVHQ4EFgQU
+ 0dKv4xTaEmdwHyox3tY8H8XVDGIwHwYDVR0jBBgwFoAURPUcR0+1NxD9b3Y0czWD
+ K1OxLHgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
+ hvcNAQELBQADggIBAJaArqLlJ2SLQ8JRwanD6LPlqQxucQ+x/LztTQpzPrsFfJds
+ E/sDZr2rXhDpz/bifIdj/DCbQ6/3w+t5JdFjT8GjXLgz1kCa/4W409FgSTgy1ENn
+ AMUU6pFIbOq/Qy/71uOTrrFWYTN5Vk+RBGxx5iDfHjDYraudi4VlcNkal4OyM98n
+ N3qp9cZD0RtWxMhvq6ahgmf9cTbEw6+l8yf/bogGLBYXXYeOoO5Q134AxrrgfthE
+ tvyKwJkT/l3OFKRcaHrebs+V1z5gPs7zWOyO5n2Z1SAmcOGfTfKMZWwp3Hi3OTr2
+ gB3LUYKyQVhC70dka3X+IbnFg5YfzJtX6YGnHlnI1SufOkEpGQDfcc0UQAWg/lgb
+ RkfMFD9tuJomBhyqv1YaxLN8yL4ZTRU0KCvvC5I5+X/zt9kBjnHlBOdYtknZT5jz
+ 7+mjqWdpmWoAjeV5+CgIzG2k7JAm6rQuE1ZQNF0wAYxPret4NHPJFqfD5gGhdrYw
+ pEUxkcwHERA/E1CkpyqUy/Hd3kqHvnEDqzFcxBdUdmOgnbpI2nAZdEpfxmA5+M1n
+ UoxQ8ZWAZH+Mdlkw/Hx5hVjGjz8snD4QN25pj/wT+V6AR5OmYb8yfsQb2S8a8yDp
+ HzcIHW+dEWpX2boirOsrdI16kNtxYqtG7c5qWBPJy5Zjkvh9qbnfT/RQx10g
+ </certificate>
+ <value>
+ zELcMKbEb82mjWdhaV62Po4Tn/fEnHg7TMQzlz3cpDP3uzGKXg4fvCa+yDYrEYqm17uywOfFQpJs
+ pVjoUMINdnYogO44mul+E+m/klzSQN3GbmvYDtKpFsSGqsymyFSg8Bv2LeDLx2Pisc3sLUhxwKN4
+ 8B6MwfZ3qUnRa5/ySk0bzEYYiRWsYR5oY7vK0kAI5c4Oi77E0W440FaEhnT7WxucFUnAhZbOSwXA
+ apE39BXu6ZbAPpTSc4f+uMErF7cRGbIODcAQJko6yjliBmvnCxj0ct7qzwUpgJojy/G5DLaVx3MF
+ Xnee9peYCrKFWpB+Z/7io0/Fqs/fDF7U25BsXA==
+ </value>
+</signature>
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index a16f118..a499472 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -54,7 +54,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -64,7 +63,6 @@
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.ForceAppStandbyTracker.Listener;
import org.junit.Before;
@@ -83,6 +81,12 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+/**
+ * Tests for {@link ForceAppStandbyTracker}
+ *
+ * Run with:
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ForceAppStandbyTrackerTest {
@@ -236,7 +240,8 @@
verify(mMockIActivityManager).registerUidObserver(
uidObserverArgumentCaptor.capture(),
eq(ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE),
+ | ActivityManager.UID_OBSERVER_ACTIVE
+ | ActivityManager.UID_OBSERVER_PROCSTATE),
eq(ActivityManager.PROCESS_STATE_UNKNOWN),
isNull());
verify(mMockIAppOpsService).startWatchingMode(
@@ -333,35 +338,39 @@
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
- assertFalse(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
- assertTrue(instance.isInForeground(Process.SYSTEM_UID));
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
mIUidObserver.onUidActive(UID_1);
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
- assertTrue(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
mIUidObserver.onUidGone(UID_1, /*disable=*/ false);
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
- assertFalse(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
mIUidObserver.onUidActive(UID_1);
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
mIUidObserver.onUidIdle(UID_1, /*disable=*/ false);
+ waitUntilMainHandlerDrain();
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
- assertFalse(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
// Toggle the app ops.
mPowerSaveMode = false;
@@ -456,6 +465,95 @@
}
@Test
+ public void testUidStateForeground() throws Exception {
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+
+ mIUidObserver.onUidActive(UID_1);
+
+ waitUntilMainHandlerDrain();
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+
+ mIUidObserver.onUidStateChanged(UID_2,
+ ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 0);
+
+ waitUntilMainHandlerDrain();
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertTrue(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+
+ mIUidObserver.onUidStateChanged(UID_1,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0);
+
+ waitUntilMainHandlerDrain();
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertTrue(instance.isUidInForeground(UID_1));
+ assertTrue(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidGone(UID_1, true);
+
+ waitUntilMainHandlerDrain();
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertTrue(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidIdle(UID_2, true);
+
+ waitUntilMainHandlerDrain();
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidStateChanged(UID_1,
+ ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0);
+
+ waitUntilMainHandlerDrain();
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertTrue(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidStateChanged(UID_1,
+ ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0);
+
+ waitUntilMainHandlerDrain();
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+ }
+
+ @Test
public void testExempt() throws Exception {
final ForceAppStandbyTrackerTestable instance = newInstance();
callStart(instance);
@@ -950,11 +1048,13 @@
mIUidObserver.onUidActive(UID_1);
mIUidObserver.onUidActive(UID_10_1);
+ waitUntilMainHandlerDrain();
+
setAppOps(UID_2, PACKAGE_2, true);
setAppOps(UID_10_2, PACKAGE_2, true);
- assertTrue(instance.isInForeground(UID_1));
- assertTrue(instance.isInForeground(UID_10_1));
+ assertTrue(instance.isUidActive(UID_1));
+ assertTrue(instance.isUidActive(UID_10_1));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
@@ -965,8 +1065,8 @@
waitUntilMainHandlerDrain();
- assertTrue(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_10_1));
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_10_1));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 045b73c..184e8de 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -27,7 +27,6 @@
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
@@ -35,12 +34,12 @@
import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
-import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED;
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED;
import static android.text.format.Time.TIMEZONE_UTC;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
+import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
import static com.google.common.truth.Truth.assertThat;
@@ -72,9 +71,9 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
-import android.app.INotificationManager;
import android.app.IUidObserver;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -107,12 +106,12 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.DataUnit;
@@ -193,6 +192,8 @@
private static final String TEST_IFACE = "test0";
private static final String TEST_SSID = "AndroidAP";
private static final String TEST_IMSI = "310210";
+ private static final int TEST_SUB_ID = 42;
+ private static final int TEST_NET_ID = 24;
private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
@@ -216,7 +217,7 @@
private @Mock INetworkManagementService mNetworkManager;
private @Mock TrustedTime mTime;
private @Mock IConnectivityManager mConnManager;
- private @Mock INotificationManager mNotifManager;
+ private @Mock NotificationManager mNotifManager;
private @Mock PackageManager mPackageManager;
private @Mock IPackageManager mIpm;
private @Mock SubscriptionManager mSubscriptionManager;
@@ -312,6 +313,8 @@
return mCarrierConfigManager;
case Context.TELEPHONY_SERVICE:
return mTelephonyManager;
+ case Context.NOTIFICATION_SERVICE:
+ return mNotifManager;
default:
return super.getSystemService(name);
}
@@ -340,7 +343,6 @@
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mStatsService,
mNetworkManager, mIpm, mTime, mPolicyDir, true);
mService.bindConnectivityManager(mConnManager);
- mService.bindNotificationManager(mNotifManager);
mPolicyListener = new NetworkPolicyListenerAnswer(mService);
// Sets some common expectations.
@@ -960,124 +962,12 @@
sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
- // TODO: consider making strongly ordered mock
- verifyPolicyDataEnable(TYPE_WIFI, true);
- verifyRemoveInterfaceQuota(TEST_IFACE);
- verifySetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ (2 * MB_IN_BYTES) - 512);
}
@Test
- public void testOverWarningLimitNotification() throws Exception {
- NetworkState[] state = null;
- NetworkStats stats = null;
- Future<String> tagFuture = null;
-
- final int CYCLE_DAY = 15;
- final long NOW = parseTime("2007-03-10T00:00Z");
- final long CYCLE_START = parseTime("2007-02-15T00:00Z");
- final long CYCLE_END = parseTime("2007-03-15T00:00Z");
-
- setCurrentTimeMillis(NOW);
-
- // assign wifi policy
- state = new NetworkState[] {};
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
-
- {
- expectCurrentTime();
- when(mConnManager.getAllNetworkState()).thenReturn(state);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
- CYCLE_END)).thenReturn(stats.getTotalBytes());
-
- mPolicyListener.expect().onMeteredIfacesChanged(any());
- setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1
- * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
- mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
- verifyPolicyDataEnable(TYPE_WIFI, true);
- }
-
- // bring up wifi network
- incrementCurrentTime(MINUTE_IN_MILLIS);
- state = new NetworkState[] { buildWifi() };
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
-
- {
- expectCurrentTime();
- when(mConnManager.getAllNetworkState()).thenReturn(state);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
- CYCLE_END)).thenReturn(stats.getTotalBytes());
-
- mPolicyListener.expect().onMeteredIfacesChanged(any());
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
- mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
- verifyPolicyDataEnable(TYPE_WIFI, true);
- verifyRemoveInterfaceQuota(TEST_IFACE);
- verifySetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
- }
-
- // go over warning, which should kick notification
- incrementCurrentTime(MINUTE_IN_MILLIS);
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1536 * KB_IN_BYTES, 15L, 0L, 0L);
-
- {
- expectCurrentTime();
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
- CYCLE_END)).thenReturn(stats.getTotalBytes());
- tagFuture = expectEnqueueNotification();
-
- mNetworkObserver.limitReached(null, TEST_IFACE);
-
- assertNotificationType(TYPE_WARNING, tagFuture.get());
- verifyPolicyDataEnable(TYPE_WIFI, true);
-
- }
-
- // go over limit, which should kick notification and dialog
- incrementCurrentTime(MINUTE_IN_MILLIS);
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 5 * MB_IN_BYTES, 512L, 0L, 0L);
-
- {
- expectCurrentTime();
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
- CYCLE_END)).thenReturn(stats.getTotalBytes());
- tagFuture = expectEnqueueNotification();
-
- mNetworkObserver.limitReached(null, TEST_IFACE);
-
- assertNotificationType(TYPE_LIMIT, tagFuture.get());
- verifyPolicyDataEnable(TYPE_WIFI, false);
- }
-
- // now snooze policy, which should remove quota
- incrementCurrentTime(MINUTE_IN_MILLIS);
-
- {
- expectCurrentTime();
- when(mConnManager.getAllNetworkState()).thenReturn(state);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
- CYCLE_END)).thenReturn(stats.getTotalBytes());
- tagFuture = expectEnqueueNotification();
-
- mPolicyListener.expect().onMeteredIfacesChanged(any());
- mService.snoozeLimit(sTemplateWifi);
- mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
- assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
- // snoozed interface still has high quota so background data is
- // still restricted.
- verifyRemoveInterfaceQuota(TEST_IFACE);
- verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
- verifyPolicyDataEnable(TYPE_WIFI, true);
- }
- }
-
- @Test
- public void testRapidNotification() throws Exception {
+ public void testNotificationWarningLimitSnooze() throws Exception {
// Create a place to store fake usage
final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
@@ -1090,38 +980,133 @@
}
});
- // Define simple data plan which gives us effectively 60MB/day
+ // Get active mobile network in place
+ expectMobileDefaults();
+ mService.updateNetworks();
+
+ // Define simple data plan
final SubscriptionPlan plan = SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
- .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_THROTTLED)
+ .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
.build();
- mService.setSubscriptionPlans(42, new SubscriptionPlan[] { plan },
+ mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
- // And get that active network in place
- when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
- new NetworkState(null, new LinkProperties(),
- new NetworkCapabilities().addTransportType(TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new StringNetworkSpecifier("42")),
- new Network(42), TEST_IMSI, null)
- });
- mService.updateNetworks();
-
// We're 20% through the month (6 days)
final long start = parseTime("2015-11-01T00:00Z");
final long end = parseTime("2015-11-07T00:00Z");
setCurrentTimeMillis(end);
+ expectCurrentTime();
- // Using 20% of data in 20% is normal
+ // Normal usage means no notification
+ {
+ history.removeBucketsBefore(Long.MAX_VALUE);
+ history.recordData(start, end,
+ new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+
+ reset(mTelephonyManager, mNetworkManager, mNotifManager);
+ expectMobileDefaults();
+
+ mService.updateNetworks();
+
+ verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ DataUnit.MEGABYTES.toBytes(1800 - 360));
+ verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
+
+ // Push over warning
+ {
+ history.removeBucketsBefore(Long.MAX_VALUE);
+ history.recordData(start, end,
+ new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+
+ reset(mTelephonyManager, mNetworkManager, mNotifManager);
+ expectMobileDefaults();
+
+ mService.updateNetworks();
+
+ verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ DataUnit.MEGABYTES.toBytes(1800 - 1799));
+ verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_WARNING),
+ isA(Notification.class), eq(UserHandle.ALL));
+ }
+
+ // Push over limit
+ {
+ history.removeBucketsBefore(Long.MAX_VALUE);
+ history.recordData(start, end,
+ new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+
+ reset(mTelephonyManager, mNetworkManager, mNotifManager);
+ expectMobileDefaults();
+
+ mService.updateNetworks();
+
+ verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(false, TEST_SUB_ID);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, 1);
+ verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT),
+ isA(Notification.class), eq(UserHandle.ALL));
+ }
+
+ // Snooze limit
+ {
+ reset(mTelephonyManager, mNetworkManager, mNotifManager);
+ expectMobileDefaults();
+
+ mService.snoozeLimit(NetworkTemplate.buildTemplateMobileAll(TEST_IMSI));
+ mService.updateNetworks();
+
+ verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ Long.MAX_VALUE);
+ verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT_SNOOZED),
+ isA(Notification.class), eq(UserHandle.ALL));
+ }
+ }
+
+ @Test
+ public void testNotificationRapid() throws Exception {
+ // Create a place to store fake usage
+ final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
+ when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+ .thenAnswer(new Answer<Long>() {
+ @Override
+ public Long answer(InvocationOnMock invocation) throws Throwable {
+ final NetworkStatsHistory.Entry entry = history.getValues(
+ invocation.getArgument(1), invocation.getArgument(2), null);
+ return entry.rxBytes + entry.txBytes;
+ }
+ });
+
+ // Get active mobile network in place
+ expectMobileDefaults();
+ mService.updateNetworks();
+
+ // Define simple data plan which gives us effectively 60MB/day
+ final SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
+ .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
+ .build();
+ mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+ mServiceContext.getOpPackageName());
+
+ // We're 20% through the month (6 days)
+ final long start = parseTime("2015-11-01T00:00Z");
+ final long end = parseTime("2015-11-07T00:00Z");
+ setCurrentTimeMillis(end);
+ expectCurrentTime();
+
+ // Using 20% data in 20% time is normal
{
history.removeBucketsBefore(Long.MAX_VALUE);
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
reset(mNotifManager);
- mService.updateNotifications();
- verify(mNotifManager, never()).enqueueNotificationWithTag(any(), any(), any(),
- anyInt(), any(), anyInt());
+ mService.updateNetworks();
+ verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
}
// Using 80% data in 20% time is alarming
@@ -1131,9 +1116,9 @@
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
reset(mNotifManager);
- mService.updateNotifications();
- verify(mNotifManager, atLeastOnce()).enqueueNotificationWithTag(any(), any(), any(),
- anyInt(), any(), anyInt());
+ mService.updateNetworks();
+ verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_RAPID),
+ isA(Notification.class), eq(UserHandle.ALL));
}
}
@@ -1165,9 +1150,8 @@
true));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
- verifyPolicyDataEnable(TYPE_WIFI, true);
- verifyRemoveInterfaceQuota(TEST_IFACE);
- verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ Long.MAX_VALUE);
}
}
@@ -1426,6 +1410,26 @@
true);
}
+ private NetworkInfo buildNetworkInfo() {
+ final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
+ TelephonyManager.NETWORK_TYPE_LTE, null, null);
+ ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ return ni;
+ }
+
+ private LinkProperties buildLinkProperties(String iface) {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ return lp;
+ }
+
+ private NetworkCapabilities buildNetworkCapabilities(int subId) {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.addTransportType(TRANSPORT_CELLULAR);
+ nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId)));
+ return nc;
+ }
+
private NetworkPolicy buildDefaultFakeMobilePolicy() {
NetworkPolicy p = mService.buildDefaultMobilePolicy(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
// set a deterministic cycle date
@@ -1478,30 +1482,21 @@
when(mTime.getCacheCertainty()).thenReturn(0L);
}
- private Future<String> expectEnqueueNotification() throws Exception {
- final FutureAnswer<String> futureAnswer = new FutureAnswer<String>(2);
- doAnswer(futureAnswer).when(mNotifManager).enqueueNotificationWithTag(
- anyString(), anyString(), anyString() /* capture here (index 2)*/,
- anyInt(), isA(Notification.class), anyInt());
- return futureAnswer;
- }
-
private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
when(mIpm.checkUidPermission(Manifest.permission.INTERNET, uid)).thenReturn(
hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
}
- private void verifySetInterfaceQuota(String iface, long quotaBytes) throws Exception {
- verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(iface, quotaBytes);
- }
-
- private void verifyRemoveInterfaceQuota(String iface) throws Exception {
- verify(mNetworkManager, atLeastOnce()).removeInterfaceQuota(iface);
- }
-
- private Future<Void> verifyPolicyDataEnable(int type, boolean enabled) throws Exception {
- // TODO: bring back this test
- return null;
+ private void expectMobileDefaults() throws Exception {
+ when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
+ new int[] { TEST_SUB_ID });
+ when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+ when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
+ new NetworkState(buildNetworkInfo(),
+ buildLinkProperties(TEST_IFACE),
+ buildNetworkCapabilities(TEST_SUB_ID),
+ new Network(TEST_NET_ID), TEST_IMSI, null)
+ });
}
private void verifyAdvisePersistThreshold() throws Exception {
@@ -1519,21 +1514,6 @@
}
}
- private static class FutureAnswer<T> extends TestAbstractFuture<T> implements Answer<Void> {
- private final int index;
-
- FutureAnswer(int index) {
- this.index = index;
- }
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- @SuppressWarnings("unchecked")
- T captured = (T) invocation.getArguments()[index];
- set(captured);
- return null;
- }
- }
-
private static void assertTimeEquals(long expected, long actual) {
if (expected != actual) {
fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index fc17da8..2378e6d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -25,6 +27,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import android.app.ActivityManager;
+import android.app.WaitResult;
import android.content.ComponentName;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -45,7 +49,7 @@
* Tests for the {@link ActivityStackSupervisor} class.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
+ * atest FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
*/
@MediumTest
@Presubmit
@@ -146,4 +150,34 @@
assertFalse(mSupervisor.mStoppingActivities.contains(firstActivity));
}
+
+ /**
+ * Ensures that waiting results are notified of launches.
+ */
+ @Test
+ public void testReportWaitingActivityLaunchedIfNeeded() throws Exception {
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+
+ // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
+ // when this happens.
+ synchronized (mSupervisor.mService) {
+ final WaitResult taskToFrontWait = new WaitResult();
+ mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
+ mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
+
+ assertTrue(mSupervisor.mWaitingActivityLaunched.isEmpty());
+ assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
+ assertEquals(taskToFrontWait.who, null);
+
+ final WaitResult deliverToTopWait = new WaitResult();
+ mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait);
+ mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity,
+ START_DELIVERED_TO_TOP);
+
+ assertTrue(mSupervisor.mWaitingActivityLaunched.isEmpty());
+ assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
+ assertEquals(deliverToTopWait.who, firstActivity.realActivity);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index deaa34c..7c3ea4f 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -25,7 +25,7 @@
/**
* Test for SyncOperation.
*
- * bit FrameworksServicesTests:com.android.server.content.SyncOperationTest
+ * atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
*/
@SmallTest
public class SyncOperationTest extends AndroidTestCase {
@@ -59,7 +59,8 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- false);
+ false,
+ /*isAppStandbyExempted=*/ false);
// Same as op1 but different time infos
SyncOperation op2 = new SyncOperation(account1, 0,
@@ -67,7 +68,8 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- false);
+ false,
+ /*isAppStandbyExempted=*/ false);
// Same as op1 but different authority
SyncOperation op3 = new SyncOperation(account1, 0,
@@ -75,7 +77,8 @@
SyncOperation.REASON_PERIODIC,
"authority2",
b1,
- false);
+ false,
+ /*isAppStandbyExempted=*/ false);
// Same as op1 but different account
SyncOperation op4 = new SyncOperation(account2, 0,
@@ -83,7 +86,8 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- false);
+ false,
+ /*isAppStandbyExempted=*/ false);
// Same as op1 but different bundle
SyncOperation op5 = new SyncOperation(account1, 0,
@@ -91,7 +95,8 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b2,
- false);
+ false,
+ /*isAppStandbyExempted=*/ false);
assertEquals(op1.key, op2.key);
assertNotSame(op1.key, op3.key);
@@ -111,7 +116,8 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- false);
+ false,
+ /*isAppStandbyExempted=*/ false);
PersistableBundle pb = op1.toJobInfoExtras();
SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
@@ -138,7 +144,8 @@
"provider", 0);
Bundle extras = new Bundle();
SyncOperation periodic = new SyncOperation(ep, 0, "package", 0, 0, extras, false, true,
- SyncOperation.NO_JOB_ID, 60000, 10000);
+ SyncOperation.NO_JOB_ID, 60000, 10000,
+ /*isAppStandbyExempted=*/ false);
SyncOperation oneoff = periodic.createOneTimeSyncOperation();
assertFalse("Conversion to oneoff sync failed.", oneoff.isPeriodic);
assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index 85de1f1..7209c79 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -31,6 +31,7 @@
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import com.android.internal.os.AtomicFile;
@@ -44,6 +45,7 @@
*
* TODO Broken. Fix it. b/62485315
*/
+@Suppress
public class SyncStorageEngineTest extends AndroidTestCase {
protected Account account1;
@@ -101,7 +103,8 @@
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL,
authority,
- Bundle.EMPTY, true);
+ Bundle.EMPTY, true,
+ /*isAppStandbyExempted=*/ false);
long historyId = engine.insertStartSyncEvent(op, time0);
long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 00e27c9..ab0bfefb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -447,5 +447,8 @@
return new TransferOwnershipMetadataManager(
new TransferOwnershipMetadataManagerTest.MockInjector());
}
+
+ @Override
+ public void runCryptoSelfTest() {}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
new file mode 100644
index 0000000..8502e69
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
@@ -0,0 +1,443 @@
+/*
+ * 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 com.android.server.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.os.SystemClock;
+import android.os.UserManager;
+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;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AmbientBrightnessStatsTrackerTest {
+
+ private TestInjector mTestInjector;
+
+ @Before
+ public void setUp() {
+ mTestInjector = new TestInjector();
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverSingleDay() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Test case where no user data
+ userStats = statsTracker.getUserStats(0);
+ assertNull(userStats);
+ // Test after adding some user data
+ statsTracker.start();
+ statsTracker.add(0, 0);
+ mTestInjector.incrementTime(1000);
+ statsTracker.stop();
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ // Test after adding some more user data
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 50000);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 2;
+ expectedStats[1] = 1.5f;
+ expectedStats[11] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverMultipleDays() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Add data for day 1
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Add data for day 2
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(0, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(0, 5);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Test that the data is tracked as expected
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(2, userStats.size());
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ expectedStats[1] = 1.5f;
+ expectedStats[3] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[4] = 5;
+ assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverMultipleUsers() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Add data for user 1
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Add data for user 2
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(1, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(1, 5);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Test that the data is tracked as expected
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ expectedStats[1] = 1.5f;
+ expectedStats[3] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ userStats = statsTracker.getUserStats(1);
+ assertEquals(1, userStats.size());
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[4] = 5;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ }
+
+ @Test
+ public void testBrightnessStatsTrackerOverMaxDays() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ // Add 10 extra days of data over the buffer limit
+ for (int i = 0; i < AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK + 10; i++) {
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(0, 10);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 20);
+ mTestInjector.incrementTime(1000);
+ statsTracker.stop();
+ }
+ // Assert that we are only tracking last "MAX_DAYS_TO_TRACK"
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK, userStats.size());
+ LocalDate runningDate = mTestInjector.getLocalDate();
+ for (int i = AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1; i >= 0; i--) {
+ assertEquals(runningDate, userStats.get(i).getLocalDate());
+ runningDate = runningDate.minusDays(1);
+ }
+ }
+
+ @Test
+ public void testReadAmbientBrightnessStats() throws IOException {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ LocalDate date = mTestInjector.getLocalDate();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ String statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ // Old stats that shouldn't be read
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+ + date.minusDays(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK)
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ // Valid stats that should get read
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+ + date.minusDays(1)
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ // Valid stats that should get read
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
+ + "0.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ statsTracker.readStats(getInputStream(statsFile));
+ userStats = statsTracker.getUserStats(0);
+ assertEquals(2, userStats.size());
+ assertEquals(new AmbientBrightnessDayStats(date.minusDays(1),
+ new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
+ new float[]{1.088f, 0, 0.726f, 0, 25.868f, 0, 0, 0, 0, 0}), userStats.get(0));
+ assertEquals(new AmbientBrightnessDayStats(date,
+ new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
+ new float[]{0, 0, 0, 0, 4.482f, 0, 0, 0, 0, 0}), userStats.get(1));
+ }
+
+ @Test
+ public void testFailedReadAmbientBrightnessStatsWithException() {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ LocalDate date = mTestInjector.getLocalDate();
+ String statsFile;
+ // Test with parse error
+ statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ // Incorrect since bucket boundaries not parsable
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"asdf,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ try {
+ statsTracker.readStats(getInputStream(statsFile));
+ } catch (IOException e) {
+ // Expected
+ }
+ assertNull(statsTracker.getUserStats(0));
+ // Test with incorrect data (bucket boundaries length not equal to stats length)
+ statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ // Correct data
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\""
+ + date.minusDays(1)
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
+ + "0.0\" />\r\n"
+ // Incorrect data
+ + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,1000.0,"
+ + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
+ + "0.0,0.0,0.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ try {
+ statsTracker.readStats(getInputStream(statsFile));
+ } catch (Exception e) {
+ // Expected
+ }
+ assertNull(statsTracker.getUserStats(0));
+ // Test with missing attribute
+ statsFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
+ + "<ambient-brightness-stats>\r\n"
+ + "<ambientBrightnessDayStats user=\"10\" local-date=\"" + date
+ + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
+ + "3000.0,10000.0\" />\r\n"
+ + "</ambient-brightness-stats>";
+ try {
+ statsTracker.readStats(getInputStream(statsFile));
+ } catch (Exception e) {
+ // Expected
+ }
+ assertNull(statsTracker.getUserStats(0));
+ }
+
+ @Test
+ public void testWriteThenReadAmbientBrightnessStats() throws IOException {
+ AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
+ ArrayList<AmbientBrightnessDayStats> userStats;
+ float[] expectedStats;
+ // Generate some dummy data
+ // Data: very old which should not be read
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Data: day 1 user 1
+ mTestInjector.incrementDate(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1);
+ statsTracker.start();
+ statsTracker.add(0, 0.05f);
+ mTestInjector.incrementTime(1000);
+ statsTracker.add(0, 0.2f);
+ mTestInjector.incrementTime(1500);
+ statsTracker.add(0, 1);
+ mTestInjector.incrementTime(2500);
+ statsTracker.stop();
+ // Data: day 1 user 2
+ statsTracker.start();
+ statsTracker.add(1, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(1, 5);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Data: day 2 user 1
+ mTestInjector.incrementDate(1);
+ statsTracker.start();
+ statsTracker.add(0, 0);
+ mTestInjector.incrementTime(3500);
+ statsTracker.add(0, 50000);
+ mTestInjector.incrementTime(5000);
+ statsTracker.stop();
+ // Write them
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ statsTracker.writeStats(baos);
+ baos.flush();
+ // Read them back and assert that it's the same
+ ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
+ AmbientBrightnessStatsTracker newStatsTracker = getTestStatsTracker();
+ newStatsTracker.readStats(input);
+ userStats = newStatsTracker.getUserStats(0);
+ assertEquals(2, userStats.size());
+ // Check day 1 user 1
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 1;
+ expectedStats[1] = 1.5f;
+ expectedStats[3] = 2.5f;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ // Check day 2 user 1
+ assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[11] = 5;
+ assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
+ userStats = newStatsTracker.getUserStats(1);
+ assertEquals(1, userStats.size());
+ // Check day 1 user 2
+ assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
+ expectedStats = getEmptyStatsArray();
+ expectedStats[0] = 3.5f;
+ expectedStats[4] = 5;
+ assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
+ }
+
+ @Test
+ public void testTimer() {
+ AmbientBrightnessStatsTracker.Timer timer = new AmbientBrightnessStatsTracker.Timer(
+ () -> mTestInjector.elapsedRealtimeMillis());
+ assertEquals(0, timer.totalDurationSec(), 0);
+ mTestInjector.incrementTime(1000);
+ assertEquals(0, timer.totalDurationSec(), 0);
+ assertFalse(timer.isRunning());
+ // Start timer
+ timer.start();
+ assertTrue(timer.isRunning());
+ assertEquals(0, timer.totalDurationSec(), 0);
+ mTestInjector.incrementTime(1000);
+ assertTrue(timer.isRunning());
+ assertEquals(1, timer.totalDurationSec(), 0);
+ // Reset timer
+ timer.reset();
+ assertEquals(0, timer.totalDurationSec(), 0);
+ assertFalse(timer.isRunning());
+ // Start again
+ timer.start();
+ assertTrue(timer.isRunning());
+ assertEquals(0, timer.totalDurationSec(), 0);
+ mTestInjector.incrementTime(2000);
+ assertTrue(timer.isRunning());
+ assertEquals(2, timer.totalDurationSec(), 0);
+ // Reset again
+ timer.reset();
+ assertEquals(0, timer.totalDurationSec(), 0);
+ assertFalse(timer.isRunning());
+ }
+
+ private class TestInjector extends AmbientBrightnessStatsTracker.Injector {
+
+ private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ private LocalDate mLocalDate = LocalDate.now();
+
+ public void incrementTime(long timeMillis) {
+ mElapsedRealtimeMillis += timeMillis;
+ }
+
+ public void incrementDate(int numDays) {
+ mLocalDate = mLocalDate.plusDays(numDays);
+ }
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public int getUserSerialNumber(UserManager userManager, int userId) {
+ return userId + 10;
+ }
+
+ @Override
+ public int getUserId(UserManager userManager, int userSerialNumber) {
+ return userSerialNumber - 10;
+ }
+
+ @Override
+ public LocalDate getLocalDate() {
+ return LocalDate.from(mLocalDate);
+ }
+ }
+
+ private AmbientBrightnessStatsTracker getTestStatsTracker() {
+ return new AmbientBrightnessStatsTracker(
+ InstrumentationRegistry.getContext().getSystemService(UserManager.class),
+ mTestInjector);
+ }
+
+ private float[] getEmptyStatsArray() {
+ return new float[AmbientBrightnessStatsTracker.BUCKET_BOUNDARIES_FOR_NEW_STATS.length];
+ }
+
+ private InputStream getInputStream(String data) {
+ return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index edc7d74..501f966 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -30,7 +30,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.database.ContentObserver;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.display.BrightnessChangeEvent;
@@ -195,7 +194,9 @@
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
final int systemUpdatedBrightness = 20;
- notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/);
+ notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/,
+ 0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/,
+ false /*isDefaultBrightnessConfig*/);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
// No events because we filtered out our change.
assertEquals(0, events.size());
@@ -285,7 +286,8 @@
+ "lastNits=\"32.333\" "
+ "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
+ "lux=\"32.2,31.1\" luxTimestamps=\""
- + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+ + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ + "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />"
+ "<event nits=\"71\" timestamp=\""
+ Long.toString(someTimeAgo) + "\" packageName=\""
+ "com.android.anapp\" user=\"11\" "
@@ -315,6 +317,9 @@
assertFalse(event.nightMode);
assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA);
assertEquals("com.example.app", event.packageName);
+ assertTrue(event.isDefaultBrightnessConfig);
+ assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
+ assertTrue(event.isUserSetBrightness);
events = tracker.getEvents(1, true).getList();
assertEquals(1, events.size());
@@ -329,6 +334,10 @@
assertEquals(3235, event.colorTemperature);
assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA);
assertEquals("com.android.anapp", event.packageName);
+ // Not present in the event so default to false.
+ assertFalse(event.isDefaultBrightnessConfig);
+ assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
+ assertFalse(event.isUserSetBrightness);
}
@Test
@@ -379,7 +388,9 @@
mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
final long secondSensorTime = mInjector.currentTimeMillis();
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
- notifyBrightnessChanged(mTracker, brightness);
+ notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
+ 0.5f /*powerPolicyDim(*/, true /*hasUserBrightnessPoints*/,
+ false /*isDefaultBrightnessConfig*/);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mTracker.writeEventsLocked(baos);
mTracker.stop();
@@ -399,6 +410,9 @@
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3339, event.colorTemperature);
+ assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
+ assertTrue(event.isUserSetBrightness);
+ assertFalse(event.isDefaultBrightnessConfig);
}
@Test
@@ -539,12 +553,16 @@
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) {
- notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/);
+ notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/,
+ 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
+ false /*isDefaultBrightnessConfig*/);
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
- boolean userInitiated) {
- tracker.notifyBrightnessChanged(brightness, userInitiated);
+ boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig) {
+ tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
+ isUserSetBrightness, isDefaultBrightnessConfig);
mInjector.waitForHandler();
}
@@ -573,7 +591,6 @@
private class TestInjector extends BrightnessTracker.Injector {
SensorEventListener mSensorListener;
BroadcastReceiver mBroadcastReceiver;
- Map<String, Integer> mSystemIntSettings = new HashMap<>();
Map<String, Integer> mSecureIntSettings = new HashMap<>();
long mCurrentTimeMillis = System.currentTimeMillis();
long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -639,7 +656,7 @@
}
@Override
- public AtomicFile getFile() {
+ public AtomicFile getFile(String filename) {
// Don't have the test write / read from anywhere.
return null;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index ce5ee13..e40e3a4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -275,7 +275,7 @@
}
@Test
- public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception {
+ public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
@@ -323,6 +323,26 @@
}
@Test
+ public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+
+ mKeySyncTask.run();
+
+ KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
+ List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
+ assertThat(applicationKeys).hasSize(1);
+ assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
+ .isEqualTo(SecureBox.encodePublicKey(
+ TestData.CERT_PATH_1.getCertificates().get(0).getPublicKey()));
+ }
+
+ @Test
public void run_setsCorrectSnapshotVersion() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index f9ffccd..0f0e3f3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -255,6 +255,9 @@
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/1/decrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/encrypt")).thenReturn(true);
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
@@ -264,6 +267,56 @@
}
@Test
+ public void getDecryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception {
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(false); // was removed
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/encrypt")).thenReturn(true); // new version is available
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
+
+ mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
+
+ verify(mKeyStoreProxy).containsAlias(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"));
+ // Attempt to get regenerated key.
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ any());
+ }
+
+ @Test
+ public void getDecryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception {
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/encrypt")).thenReturn(false); // was removed
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
+
+ mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
+
+ verify(mKeyStoreProxy).containsAlias(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"));
+ // Attempt to get regenerated key.
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ any());
+ }
+
+ @Test
public void getEncryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
@@ -274,7 +327,13 @@
+ "platform/42/1/encrypt")).thenReturn(true);
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/2/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
@@ -295,11 +354,16 @@
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(false); // was removed.
-
+ + "platform/42/1/encrypt")).thenReturn(true);
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true); // new version is available
+ + "platform/42/1/decrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
@@ -312,14 +376,45 @@
}
@Test
- public void getEncryptKey_generatesNewKeyIfOldWasRemoved() throws Exception {
+ public void getEncryptKey_generatesNewKeyIfDecryptKeyIsUnrecoverable() throws Exception {
+ doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any());
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(false); // was removed.
-
+ + "platform/42/1/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(false); // was removed
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/2/encrypt")).thenReturn(true); // new version is available
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
+
+ mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
+
+ // Attempt to get regenerated key.
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ any());
+ }
+
+ @Test
+ public void getEncryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception {
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(false); // was removed
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/encrypt")).thenReturn(true); // new version is available
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
@@ -332,10 +427,38 @@
}
@Test
- public void getEncryptKey_getsEndryptKeyWithCorrectAlias() throws Exception {
+ public void getEncryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception {
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/encrypt")).thenReturn(false); // was removed
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/2/decrypt")).thenReturn(true);
+
+ mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
+
+ verify(mKeyStoreProxy).containsAlias(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"));
+ // Attempt to get regenerated key.
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ any());
+ }
+
+ @Test
+ public void getEncryptKey_getsEncryptKeyWithCorrectAlias() throws Exception {
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/1/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy
+ .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ + "platform/42/1/decrypt")).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index c863aab..a523b86 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -39,6 +39,7 @@
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
+import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -49,6 +50,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -123,6 +125,7 @@
private static final byte[] RECOVERY_RESPONSE_HEADER =
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final String TEST_ALIAS = "nick";
+ private static final String TEST_ALIAS2 = "bob";
private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
private static final int GENERATION_ID = 1;
private static final byte[] NONCE = getUtf8Bytes("nonce");
@@ -135,6 +138,8 @@
@Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
@Mock private KeyguardManager mKeyguardManager;
@Mock private PlatformKeyManager mPlatformKeyManager;
+ @Mock private KeyStore mKeyStore;
+ @Mock private ApplicationKeyStorage mApplicationKeyStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
@@ -164,12 +169,14 @@
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
+ mKeyStore,
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
mRecoverySnapshotStorage,
mMockListenersStorage,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mApplicationKeyStorage);
}
@After
@@ -231,15 +238,81 @@
}
@Test
- public void initRecoveryService_updatesShouldCreateSnapshot() throws Exception {
+ public void initRecoveryService_succeeds() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
- // Sync is not needed.
+ long certSerial = 1000L;
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_1);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
+ certSerial);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
+ }
+
+ @Test
+ public void initRecoveryService_updatesWithLargerSerial() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial + 1));
+
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
+ .isEqualTo(certSerial + 1);
+ }
+
+ @Test
+ public void initRecoveryService_ignoresSmallerSerial() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial - 1));
+
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
+ .isEqualTo(certSerial);
+ }
+
+ @Test
+ public void initRecoveryService_ignoresTheSameSerial() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+
+ // If the second update succeeds, getShouldCreateSnapshot() will return true.
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+ }
+
+ @Test
+ public void initRecoveryService_succeedsWithRawPublicKey() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNotNull();
}
@Test
@@ -337,26 +410,6 @@
}
@Test
- public void startRecoverySession_throwsIfBadKey() throws Exception {
- try {
- mRecoverableKeyStoreManager.startRecoverySession(
- TEST_SESSION_ID,
- getUtf8Bytes("0"),
- TEST_VAULT_PARAMS,
- TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
- fail("should have thrown");
- } catch (ServiceSpecificException e) {
- assertEquals("Not a valid X509 key", e.getMessage());
- }
- }
-
- @Test
public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception {
byte[] vaultParams = TEST_VAULT_PARAMS.clone();
vaultParams[1] ^= (byte) 1; // Flip 1 bit
@@ -418,7 +471,7 @@
}
@Test
- public void recoverKeys_throwsIfFailedToDecryptAnApplicationKey() throws Exception {
+ public void recoverKeys_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
TEST_PUBLIC_KEY,
@@ -436,7 +489,7 @@
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
WrappedApplicationKey badApplicationKey = new WrappedApplicationKey(
TEST_ALIAS,
- randomBytes(32));
+ encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)));
try {
mRecoverableKeyStoreManager.recoverKeys(
@@ -445,7 +498,7 @@
/*applicationKeys=*/ ImmutableList.of(badApplicationKey));
fail("should have thrown");
} catch (ServiceSpecificException e) {
- assertThat(e.getMessage()).startsWith("Failed to recover key with alias 'nick'");
+ assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys");
}
}
@@ -481,6 +534,44 @@
}
@Test
+ public void recoverKeys_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySession(
+ TEST_SESSION_ID,
+ TEST_PUBLIC_KEY,
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(new KeyChainProtectionParams(
+ TYPE_LOCKSCREEN,
+ UI_FORMAT_PASSWORD,
+ KeyDerivationParams.createSha256Params(TEST_SALT),
+ TEST_SECRET)));
+ byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
+ .getKeyClaimant();
+ SecretKey recoveryKey = randomRecoveryKey();
+ byte[] encryptedClaimResponse = encryptClaimResponse(
+ keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
+
+ byte[] applicationKeyBytes1 = randomBytes(32);
+ byte[] applicationKeyBytes2 = randomBytes(32);
+
+ WrappedApplicationKey applicationKey1 = new WrappedApplicationKey(
+ TEST_ALIAS,
+ // Use a different recovery key here, so the decryption will fail
+ encryptedApplicationKey(randomRecoveryKey(), applicationKeyBytes1));
+ WrappedApplicationKey applicationKey2 = new WrappedApplicationKey(
+ TEST_ALIAS2,
+ encryptedApplicationKey(recoveryKey, applicationKeyBytes2));
+
+ Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
+ TEST_SESSION_ID,
+ encryptedClaimResponse,
+ ImmutableList.of(applicationKey1, applicationKey2));
+
+ assertThat(recoveredKeys).hasSize(1);
+ assertThat(recoveredKeys.get(TEST_ALIAS2)).isEqualTo(applicationKeyBytes2);
+ }
+
+ @Test
public void setSnapshotCreatedPendingIntent() throws Exception {
int uid = Binder.getCallingUid();
PendingIntent intent = PendingIntent.getBroadcast(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
new file mode 100644
index 0000000..0e4f91b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -0,0 +1,202 @@
+package com.android.server.locksettings.recoverablekeystore;
+
+import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+
+public final class TestData {
+
+ private static final String CERT_PATH_ENCODING = "PkiPath";
+
+ private static final String CERT_PATH_1_BASE64 = ""
+ + "MIIIPzCCBS8wggMXoAMCAQICAhAAMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNVBAMM"
+ + "FUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDMwMDQyMDNaFw0yODAyMDEw"
+ + "MDQyMDNaMC0xKzApBgNVBAMMIkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnRlcm1l"
+ + "ZGlhdGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDckHib0X6rQyDq"
+ + "k4519b5du0OrCPk30XXKwz+Hz5y4cGZaWKGcHOHWS2X9YApRzO00/EbvFkWVUTVG"
+ + "27wJ54V+C3HHSOAUWHhEgfFWvvHwfn9HTDx1BEk79aQqJ7DuJ06Sn/WOiMtKVAT5"
+ + "6Mi8mekBxpMOrdZqwlcLrUVsZxEHsw5/ceZu4cSWzc7SzlnbNK1cCgyRDGqWf6Gp"
+ + "3hGE86kUOtM1i95RgUIpw+w/z0wxpF6kIyQTjK+KjiYH/RBOJIEcm6sSWZlMotKL"
+ + "Sn2lhf+XL8yUxExIHTosfeb077QWW4w2BB2NZM4wPAO3w4aw33FNigDQc2SQYmnU"
+ + "EYmIcD8kx77+JWCgCxBJc2zTHXtBxWuXAQ+iegt8RO+QD97pd6XKM9xPsAOkcWLp"
+ + "79o+AJol4P5fwvgYM69mM4lwH12v86RI4aptPQOag0KDIHXyKbjaQyAgv30l4KkD"
+ + "pf2uWODhOOTwNbVPYUm3sYUlhBcbyhTk8YqN9sPU4QAao5sKTAYZgB/mlheQypTU"
+ + "wyvqz6bRzGehVB3ltP9gCyKdI04VXEUuUBWk3STyV2REQen5/LKAns6v11Cz22Zr"
+ + "EdCvNLgetnyV7CJsOa/wD/GiUWL2Ta7pzshi9ahJqrrcNPRbAzOLcNKZkFexhzPp"
+ + "onuo/pNrcaRda1frepXxVkmbsgOULwIDAQABo2YwZDAdBgNVHQ4EFgQUd6md2hCP"
+ + "lmf3VkEX5FfDxKBLbaAwHwYDVR0jBBgwFoAUm2X66jmB+eBCaZHSjGYzHM/x6fgw"
+ + "EgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL"
+ + "BQADggIBAFgShhuW+WVTowN080PLf0TWPlHACHHUPghf7rFGxgUjJypCloE84Beg"
+ + "3ROpP5l19CDqZ9OyPzA1z6VAzeGXyFhZvby7G2tZDBRP/v0u8pnSAdC5F8l8Vh2Y"
+ + "GdgE3sZD25vpdBi7P0Ef6LYQetOJXn86PqgmgW1F6lzxDjKCsi9kpeU0AWwDdOVg"
+ + "748wku50o8UEzsVwxzFd9toGlge/nn3FH5J7EuGzAlFwToHqpwTVEegaAd0l9mr5"
+ + "+rS7Urd3X80BHDqCBcXE7Uqbtzw5Y+lmowMCnW0kFN02dC9dLt2c9IxC+9sPIA5e"
+ + "TkrZBkrkTVRGLj2r29j7nC9m5VaKcBqcLZDWy8pRna8yaZprgNdE8d/WTY9nVsic"
+ + "09N8zNF5Q0bhhWa3QonlB9XW5ZqDguiclvn+5TtREzSAtSOyxM+gfG3l0wjOywIk"
+ + "1aFa52RaqAWPL67KOM6G3vKNpMnW5hrmHrijuKxiarGIoZfkZMR5ijK0uFgv3/p6"
+ + "NHL/YQBaHJJhkKet5ThiPxwW9+1k/ZcXVeY26Xh+22Gp/8to7ZW8guPPiN1hfpD+"
+ + "7f1IdSmHDrsZQQ7bfzV0bppsyNNB7e2Ecyw+GQny27nytBLJDGdRBurbwQvzppQO"
+ + "6Qmlk0rfCszh7bGCoCQNxXmuDsQ5BC+pQUqJplTqds1smyi29xs3MIIDCDCB8aAD"
+ + "AgECAgYBYVkuU0cwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwiR29vZ2xlIENy"
+ + "eXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAyMDIwMTAxMDNaFw0yMDAy"
+ + "MDMwMTAxMDNaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnN0"
+ + "YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfButJT+htocB40B"
+ + "tDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl4b0hRH54Uvow"
+ + "DQYJKoZIhvcNAQELBQADggIBAJ3PM4GNTNYzMr8E/IGsWZkLx9ARAALqBXz7As59"
+ + "F8y5UcLMqkXD/ewOfBZgF5VzjlAePyE/wSw0wc3xzvrDVVDiZaMBW1DVtSlbn25q"
+ + "00m00mmcUeyyMc7vuRkPoDshIMQTc8+U3yyYsVScSV+B4TvSx6wPZ9FpwnSPjVPD"
+ + "2GkqeMTWszuxNVEWq0wmm0K5lMaX0hfiak+4/IZxOPPGIg2py1KLA/H2gdyeqyJR"
+ + "cAsyEkfwLlushR5T9abSiPsIRcYoX8Ck8Lt+gQ7RCMefnm8CoOBKIfcjuV4PGOoe"
+ + "Xrq57VR5SsOeT07bL+D7B+mohYFI1v2G3WClAE8XgM3q8NoFFvaYmoi0+UcTduil"
+ + "47qvozjdNmjRAgu5j6vMKXEdG5Rqsja8hy0LG1hwfnR0gNiwcZ5Le3GyFnwH1Igq"
+ + "vsGOUM0ohnDUAU0zJY7nG0QYrDYe5/QPRNhWDpYkwHDiqcG28wIQCOTPAZHU2EoS"
+ + "KjSqEG2l0S5JPcor2BEde9ikSkcmK8foxlOHIdFn+n7RNF3bSEfKn1IOuXoqPidm"
+ + "eBQLevqG8KTy/C9CHqlaCNlpbIA9h+WVfsjm2s6JXBu0YbcfoIbJAmSuZVeqB/+Z"
+ + "Vvpfiad/jQWzY49fRnsSmV7VveTFPGtJxC89EadbMAinMZo+72u59319RqN5wsP2"
+ + "Zus8";
+ private static String CERT_PATH_2_BASE64 = ""
+ + "MIIFMzCCBS8wggMXoAMCAQICAhAAMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNVBAMM"
+ + "FUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDMwMDQyMDNaFw0yODAyMDEw"
+ + "MDQyMDNaMC0xKzApBgNVBAMMIkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnRlcm1l"
+ + "ZGlhdGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDckHib0X6rQyDq"
+ + "k4519b5du0OrCPk30XXKwz+Hz5y4cGZaWKGcHOHWS2X9YApRzO00/EbvFkWVUTVG"
+ + "27wJ54V+C3HHSOAUWHhEgfFWvvHwfn9HTDx1BEk79aQqJ7DuJ06Sn/WOiMtKVAT5"
+ + "6Mi8mekBxpMOrdZqwlcLrUVsZxEHsw5/ceZu4cSWzc7SzlnbNK1cCgyRDGqWf6Gp"
+ + "3hGE86kUOtM1i95RgUIpw+w/z0wxpF6kIyQTjK+KjiYH/RBOJIEcm6sSWZlMotKL"
+ + "Sn2lhf+XL8yUxExIHTosfeb077QWW4w2BB2NZM4wPAO3w4aw33FNigDQc2SQYmnU"
+ + "EYmIcD8kx77+JWCgCxBJc2zTHXtBxWuXAQ+iegt8RO+QD97pd6XKM9xPsAOkcWLp"
+ + "79o+AJol4P5fwvgYM69mM4lwH12v86RI4aptPQOag0KDIHXyKbjaQyAgv30l4KkD"
+ + "pf2uWODhOOTwNbVPYUm3sYUlhBcbyhTk8YqN9sPU4QAao5sKTAYZgB/mlheQypTU"
+ + "wyvqz6bRzGehVB3ltP9gCyKdI04VXEUuUBWk3STyV2REQen5/LKAns6v11Cz22Zr"
+ + "EdCvNLgetnyV7CJsOa/wD/GiUWL2Ta7pzshi9ahJqrrcNPRbAzOLcNKZkFexhzPp"
+ + "onuo/pNrcaRda1frepXxVkmbsgOULwIDAQABo2YwZDAdBgNVHQ4EFgQUd6md2hCP"
+ + "lmf3VkEX5FfDxKBLbaAwHwYDVR0jBBgwFoAUm2X66jmB+eBCaZHSjGYzHM/x6fgw"
+ + "EgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL"
+ + "BQADggIBAFgShhuW+WVTowN080PLf0TWPlHACHHUPghf7rFGxgUjJypCloE84Beg"
+ + "3ROpP5l19CDqZ9OyPzA1z6VAzeGXyFhZvby7G2tZDBRP/v0u8pnSAdC5F8l8Vh2Y"
+ + "GdgE3sZD25vpdBi7P0Ef6LYQetOJXn86PqgmgW1F6lzxDjKCsi9kpeU0AWwDdOVg"
+ + "748wku50o8UEzsVwxzFd9toGlge/nn3FH5J7EuGzAlFwToHqpwTVEegaAd0l9mr5"
+ + "+rS7Urd3X80BHDqCBcXE7Uqbtzw5Y+lmowMCnW0kFN02dC9dLt2c9IxC+9sPIA5e"
+ + "TkrZBkrkTVRGLj2r29j7nC9m5VaKcBqcLZDWy8pRna8yaZprgNdE8d/WTY9nVsic"
+ + "09N8zNF5Q0bhhWa3QonlB9XW5ZqDguiclvn+5TtREzSAtSOyxM+gfG3l0wjOywIk"
+ + "1aFa52RaqAWPL67KOM6G3vKNpMnW5hrmHrijuKxiarGIoZfkZMR5ijK0uFgv3/p6"
+ + "NHL/YQBaHJJhkKet5ThiPxwW9+1k/ZcXVeY26Xh+22Gp/8to7ZW8guPPiN1hfpD+"
+ + "7f1IdSmHDrsZQQ7bfzV0bppsyNNB7e2Ecyw+GQny27nytBLJDGdRBurbwQvzppQO"
+ + "6Qmlk0rfCszh7bGCoCQNxXmuDsQ5BC+pQUqJplTqds1smyi29xs3";
+
+ private static final String THM_CERT_XML_BEFORE_SERIAL = ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<certificates>\n"
+ + " <metadata>\n"
+ + " <serial>\n";
+ private static final String THM_CERT_XML_AFTER_SERIAL = ""
+ + " </serial>\n"
+ + " <creation-time>\n"
+ + " 1515697631\n"
+ + " </creation-time>\n"
+ + " <refresh-interval>\n"
+ + " 2592000\n"
+ + " </refresh-interval>\n"
+ + " <previous>\n"
+ + " <serial>\n"
+ + " 0\n"
+ + " </serial>\n"
+ + " <hash>\n"
+ + " 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=\n"
+ + " </hash>\n"
+ + " </previous>\n"
+ + " </metadata>\n"
+ + " <intermediates>\n"
+ + " <cert>\n"
+ + " MIIFLzCCAxegAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVR29v\n"
+ + " Z2xlIENyeXB0QXV0aFZhdWx0MB4XDTE4MDIwMzAwNDIwM1oXDTI4MDIwMTAwNDIw\n"
+ + " M1owLTErMCkGA1UEAwwiR29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0\n"
+ + " ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANyQeJvRfqtDIOqTjnX1\n"
+ + " vl27Q6sI+TfRdcrDP4fPnLhwZlpYoZwc4dZLZf1gClHM7TT8Ru8WRZVRNUbbvAnn\n"
+ + " hX4LccdI4BRYeESB8Va+8fB+f0dMPHUESTv1pConsO4nTpKf9Y6Iy0pUBPnoyLyZ\n"
+ + " 6QHGkw6t1mrCVwutRWxnEQezDn9x5m7hxJbNztLOWds0rVwKDJEMapZ/oaneEYTz\n"
+ + " qRQ60zWL3lGBQinD7D/PTDGkXqQjJBOMr4qOJgf9EE4kgRybqxJZmUyi0otKfaWF\n"
+ + " /5cvzJTETEgdOix95vTvtBZbjDYEHY1kzjA8A7fDhrDfcU2KANBzZJBiadQRiYhw\n"
+ + " PyTHvv4lYKALEElzbNMde0HFa5cBD6J6C3xE75AP3ul3pcoz3E+wA6RxYunv2j4A\n"
+ + " miXg/l/C+Bgzr2YziXAfXa/zpEjhqm09A5qDQoMgdfIpuNpDICC/fSXgqQOl/a5Y\n"
+ + " 4OE45PA1tU9hSbexhSWEFxvKFOTxio32w9ThABqjmwpMBhmAH+aWF5DKlNTDK+rP\n"
+ + " ptHMZ6FUHeW0/2ALIp0jThVcRS5QFaTdJPJXZERB6fn8soCezq/XULPbZmsR0K80\n"
+ + " uB62fJXsImw5r/AP8aJRYvZNrunOyGL1qEmqutw09FsDM4tw0pmQV7GHM+mie6j+\n"
+ + " k2txpF1rV+t6lfFWSZuyA5QvAgMBAAGjZjBkMB0GA1UdDgQWBBR3qZ3aEI+WZ/dW\n"
+ + " QRfkV8PEoEttoDAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DASBgNV\n"
+ + " HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC\n"
+ + " AgEAWBKGG5b5ZVOjA3TzQ8t/RNY+UcAIcdQ+CF/usUbGBSMnKkKWgTzgF6DdE6k/\n"
+ + " mXX0IOpn07I/MDXPpUDN4ZfIWFm9vLsba1kMFE/+/S7ymdIB0LkXyXxWHZgZ2ATe\n"
+ + " xkPbm+l0GLs/QR/othB604lefzo+qCaBbUXqXPEOMoKyL2Sl5TQBbAN05WDvjzCS\n"
+ + " 7nSjxQTOxXDHMV322gaWB7+efcUfknsS4bMCUXBOgeqnBNUR6BoB3SX2avn6tLtS\n"
+ + " t3dfzQEcOoIFxcTtSpu3PDlj6WajAwKdbSQU3TZ0L10u3Zz0jEL72w8gDl5OStkG\n"
+ + " SuRNVEYuPavb2PucL2blVopwGpwtkNbLylGdrzJpmmuA10Tx39ZNj2dWyJzT03zM\n"
+ + " 0XlDRuGFZrdCieUH1dblmoOC6JyW+f7lO1ETNIC1I7LEz6B8beXTCM7LAiTVoVrn\n"
+ + " ZFqoBY8vrso4zobe8o2kydbmGuYeuKO4rGJqsYihl+RkxHmKMrS4WC/f+no0cv9h\n"
+ + " AFockmGQp63lOGI/HBb37WT9lxdV5jbpeH7bYan/y2jtlbyC48+I3WF+kP7t/Uh1\n"
+ + " KYcOuxlBDtt/NXRummzI00Ht7YRzLD4ZCfLbufK0EskMZ1EG6tvBC/OmlA7pCaWT\n"
+ + " St8KzOHtsYKgJA3Fea4OxDkEL6lBSommVOp2zWybKLb3Gzc=\n"
+ + " </cert>\n"
+ + " </intermediates>\n"
+ + " <endpoints>\n"
+ + " <cert>\n"
+ + " MIIDCDCB8aADAgECAgYBYVkuU0cwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi\n"
+ + " R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAyMDIwMTAx\n"
+ + " MDNaFw0yMDAyMDMwMTAxMDNaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW\n"
+ + " YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu\n"
+ + " tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl\n"
+ + " 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBAJ3PM4GNTNYzMr8E/IGsWZkLx9AR\n"
+ + " AALqBXz7As59F8y5UcLMqkXD/ewOfBZgF5VzjlAePyE/wSw0wc3xzvrDVVDiZaMB\n"
+ + " W1DVtSlbn25q00m00mmcUeyyMc7vuRkPoDshIMQTc8+U3yyYsVScSV+B4TvSx6wP\n"
+ + " Z9FpwnSPjVPD2GkqeMTWszuxNVEWq0wmm0K5lMaX0hfiak+4/IZxOPPGIg2py1KL\n"
+ + " A/H2gdyeqyJRcAsyEkfwLlushR5T9abSiPsIRcYoX8Ck8Lt+gQ7RCMefnm8CoOBK\n"
+ + " IfcjuV4PGOoeXrq57VR5SsOeT07bL+D7B+mohYFI1v2G3WClAE8XgM3q8NoFFvaY\n"
+ + " moi0+UcTduil47qvozjdNmjRAgu5j6vMKXEdG5Rqsja8hy0LG1hwfnR0gNiwcZ5L\n"
+ + " e3GyFnwH1IgqvsGOUM0ohnDUAU0zJY7nG0QYrDYe5/QPRNhWDpYkwHDiqcG28wIQ\n"
+ + " COTPAZHU2EoSKjSqEG2l0S5JPcor2BEde9ikSkcmK8foxlOHIdFn+n7RNF3bSEfK\n"
+ + " n1IOuXoqPidmeBQLevqG8KTy/C9CHqlaCNlpbIA9h+WVfsjm2s6JXBu0YbcfoIbJ\n"
+ + " AmSuZVeqB/+ZVvpfiad/jQWzY49fRnsSmV7VveTFPGtJxC89EadbMAinMZo+72u5\n"
+ + " 9319RqN5wsP2Zus8\n"
+ + " </cert>\n"
+ + " </endpoints>\n"
+ + "</certificates>\n";
+
+ public static byte[] getCertPath1Bytes() {
+ try {
+ return CertUtils.decodeBase64(CERT_PATH_1_BASE64);
+ } catch (Exception e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static byte[] getCertPath2Bytes() {
+ try {
+ return CertUtils.decodeBase64(CERT_PATH_2_BASE64);
+ } catch (Exception e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static final CertPath CERT_PATH_1;
+ public static final CertPath CERT_PATH_2;
+
+ static {
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ CERT_PATH_1 = certFactory.generateCertPath(
+ new ByteArrayInputStream(getCertPath1Bytes()), CERT_PATH_ENCODING);
+ CERT_PATH_2 = certFactory.generateCertPath(
+ new ByteArrayInputStream(getCertPath2Bytes()), CERT_PATH_ENCODING);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static byte[] getCertXmlWithSerial(long serial) {
+ String xml = THM_CERT_XML_BEFORE_SERIAL + serial + THM_CERT_XML_AFTER_SERIAL;
+ return xml.getBytes(StandardCharsets.UTF_8);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
new file mode 100644
index 0000000..d08dab4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
@@ -0,0 +1,356 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.InputStream;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.w3c.dom.Element;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class CertUtilsTest {
+
+ private static final String XML_STR = ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<!-- comment 1 -->"
+ + "<root>\n\n\n\r\r\r"
+ + " <node1>\r\n\r\n"
+ + " node1-1</node1>"
+ + " <!-- comment 2 -->"
+ + " <node1>node1-2"
+ + " \n\r\n\r</node1>"
+ + " <node2>"
+ + " <node1> node2-node1-1</node1>"
+ + " <node1>node2-node1-2 </node1>"
+ + " <!-- comment 3 -->"
+ + " <node1> node2-node1-3 </node1>"
+ + " </node2>"
+ + "</root>";
+
+ private static final String SIGNED_STR = "abcdefg\n";
+ private static final String SIGNATURE_BASE64 = ""
+ + "KxBt9B3pwL3/59SrjTJTpuhc9JRxLOUNwNr3J4EEdXj5BqkYOUeXIOjyBGp8XaOnmuW8WmBxhko3"
+ + "yTR3/M9x0/pJuKDgqQSSFG+I56O/IAri7DmMBfY8QqcgiF8RaR86G7mWXUIdu8ixEtpKa//T4bN7"
+ + "c8Txvt96ApAcW0wJDihfCqDEXyi56pFCp+qEZuL4fS8iZtZTUkvxim1tb2/IsZ9OyDd9BWxp+JTs"
+ + "zihzH6xqnUCa1aELSUZnU8OzWGeuKpVDQDbDMtQpcxJ9o+6L6wO5vmQutZAulgw5gRPGhYWVs8+0"
+ + "ATdNEbv8TSomkXkZ3/lMYnmPXKmaHxcP4330DA==";
+ private static final PublicKey SIGNER_PUBLIC_KEY = TestData.INTERMEDIATE_CA_2.getPublicKey();
+
+ @Test
+ public void decodeCert_readPemFile_succeeds_singleBlock() throws Exception {
+ InputStream f = TestData.openTestFile("pem/valid-cert.pem");
+ X509Certificate cert = CertUtils.decodeCert(f);
+ assertThat(cert).isEqualTo(TestData.ROOT_CA_TRUSTED);
+ }
+
+ @Test
+ public void decodeCert_readPemFile_succeeds_multipleBlocks() throws Exception {
+ InputStream in = TestData.openTestFile("pem/valid-cert-multiple-blocks.pem");
+ X509Certificate cert = CertUtils.decodeCert(in);
+ assertThat(cert).isEqualTo(TestData.ROOT_CA_TRUSTED);
+ }
+
+ @Test
+ public void decodeCert_readPemFile_throwsIfNoBeginEndLines() throws Exception {
+ InputStream in = TestData.openTestFile("pem/invalid-cert-1-no-begin-end.pem");
+ assertThrows(CertParsingException.class, () -> CertUtils.decodeCert(in));
+ }
+
+ @Test
+ public void decodeCert_readPemFile_throwsIfEmptyBlock() throws Exception {
+ InputStream in = TestData.openTestFile("pem/invalid-cert-2-empty-block.pem");
+ assertThrows(CertParsingException.class, () -> CertUtils.decodeCert(in));
+ }
+
+ @Test
+ public void decodeCert_readPemFile_throwsIfInvalidCert() throws Exception {
+ InputStream in = TestData.openTestFile("pem/invalid-cert-3-invalid-key.pem");
+ assertThrows(CertParsingException.class, () -> CertUtils.decodeCert(in));
+ }
+
+ @Test
+ public void decodeCert_readBytes_succeeds() throws Exception {
+ X509Certificate cert = CertUtils.decodeCert(TestData.INTERMEDIATE_CA_2.getEncoded());
+ assertThat(cert.getIssuerX500Principal().getName())
+ .isEqualTo("CN=Google CryptAuthVault Intermediate");
+ }
+
+ @Test
+ public void decodeCert_readBytes_throwsIfInvalidCert() throws Exception {
+ byte[] modifiedCertBytes = TestData.INTERMEDIATE_CA_1.getEncoded();
+ modifiedCertBytes[0] ^= (byte) 1;
+ assertThrows(CertParsingException.class, () -> CertUtils.decodeCert(modifiedCertBytes));
+ }
+
+ @Test
+ public void decodeBase64_succeeds() throws Exception {
+ assertThat(CertUtils.decodeBase64("VEVTVA==")).isEqualTo("TEST".getBytes(UTF_8));
+ }
+
+ @Test
+ public void decodeBase64_succeedsIfEmptyInput() throws Exception {
+ assertThat(CertUtils.decodeBase64("")).hasLength(0);
+ }
+
+ @Test
+ public void decodeBase64_throwsIfInvalidInput() throws Exception {
+ assertThrows(CertParsingException.class, () -> CertUtils.decodeBase64("EVTVA=="));
+ }
+
+ @Test
+ public void getXmlRootNode_succeeds() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ assertThat(root.getTagName()).isEqualTo("root");
+ }
+
+ @Test
+ public void getXmlRootNode_throwsIfEmptyInput() throws Exception {
+ assertThrows(CertParsingException.class, () -> CertUtils.getXmlRootNode(new byte[0]));
+ }
+
+ @Test
+ public void getXmlNodeContents_singleLevel_succeeds() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ assertThat(CertUtils.getXmlNodeContents(CertUtils.MUST_EXIST_UNENFORCED, root, "node1"))
+ .containsExactly("node1-1", "node1-2");
+ }
+
+ @Test
+ public void getXmlNodeContents_multipleLevels_succeeds() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ assertThat(CertUtils.getXmlNodeContents(CertUtils.MUST_EXIST_UNENFORCED, root, "node2", "node1"))
+ .containsExactly("node2-node1-1", "node2-node1-2", "node2-node1-3");
+ }
+
+ @Test
+ public void getXmlNodeContents_mustExistFalse_succeedsIfNotExist() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ assertThat(
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_UNENFORCED, root, "node2", "node-not-exist"))
+ .isEmpty();
+ }
+
+ @Test
+ public void getXmlNodeContents_mustExistAtLeastOne_throwsIfNotExist() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_AT_LEAST_ONE, root, "node2",
+ "node-not-exist"));
+ assertThat(expected.getMessage()).contains("must contain at least one");
+ }
+
+ @Test
+ public void getXmlNodeContents_mustExistExactlyOne_throwsIfNotExist() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_EXACTLY_ONE, root, "node-not-exist",
+ "node1"));
+ assertThat(expected.getMessage()).contains("must contain exactly one");
+ }
+
+ @Test
+ public void getXmlNodeContents_mustExistExactlyOne_throwsIfMultipleExist() throws Exception {
+ Element root = CertUtils.getXmlRootNode(XML_STR.getBytes(UTF_8));
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertUtils.getXmlNodeContents(
+ CertUtils.MUST_EXIST_EXACTLY_ONE, root, "node2", "node1"));
+ assertThat(expected.getMessage()).contains("must contain exactly one");
+ }
+
+ @Test
+ public void verifyRsaSha256Signature_succeeds() throws Exception {
+ CertUtils.verifyRsaSha256Signature(
+ SIGNER_PUBLIC_KEY,
+ Base64.getDecoder().decode(SIGNATURE_BASE64),
+ SIGNED_STR.getBytes(UTF_8));
+ }
+
+ @Test
+ public void verifyRsaSha256Signature_throwsIfMismatchSignature() throws Exception {
+ byte[] modifiedBytes = SIGNED_STR.getBytes(UTF_8);
+ modifiedBytes[0] ^= (byte) 1;
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ CertUtils.verifyRsaSha256Signature(
+ SIGNER_PUBLIC_KEY, Base64.getDecoder().decode(SIGNATURE_BASE64),
+ modifiedBytes));
+ }
+
+ @Test
+ public void verifyRsaSha256Signature_throwsIfWrongKeyType() throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
+ keyPairGenerator.initialize(new ECGenParameterSpec("secp256r1"));
+ PublicKey publicKey = keyPairGenerator.generateKeyPair().getPublic();
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ CertUtils.verifyRsaSha256Signature(
+ publicKey,
+ Base64.getDecoder().decode(SIGNATURE_BASE64),
+ SIGNED_STR.getBytes(UTF_8)));
+ }
+
+ @Test
+ public void buildCertPath_succeedsWithoutIntermediates() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
+ X509Certificate leafCert = TestData.INTERMEDIATE_CA_1;
+ CertPath certPath = CertUtils.buildCertPath(
+ CertUtils.buildPkixParams(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, Collections.emptyList(),
+ leafCert));
+ assertThat(certPath.getCertificates()).containsExactly(
+ TestData.INTERMEDIATE_CA_1).inOrder();
+ }
+
+ @Test
+ public void buildCertPath_succeedsWithIntermediates() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_2;
+ CertPath certPath =
+ CertUtils.buildCertPath(
+ CertUtils.buildPkixParams(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert));
+ assertThat(certPath.getCertificates())
+ .containsExactly(
+ TestData.LEAF_CERT_2, TestData.INTERMEDIATE_CA_2,
+ TestData.INTERMEDIATE_CA_1)
+ .inOrder();
+ }
+
+ @Test
+ public void buildCertPath_succeedsWithIntermediates_ignoreUnrelatedIntermedateCert()
+ throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_1;
+ CertPath certPath =
+ CertUtils.buildCertPath(
+ CertUtils.buildPkixParams(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert));
+ assertThat(certPath.getCertificates())
+ .containsExactly(TestData.LEAF_CERT_1, TestData.INTERMEDIATE_CA_1)
+ .inOrder();
+ }
+
+ @Test
+ public void buildCertPath_throwsIfWrongRootCommonName() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_DIFFERENT_COMMON_NAME;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_1;
+
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ CertUtils.buildCertPath(
+ CertUtils.buildPkixParams(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert)));
+ }
+
+ @Test
+ public void buildCertPath_throwsIfMissingIntermediateCert() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_DIFFERENT_COMMON_NAME;
+ List<X509Certificate> intermediateCerts = Collections.emptyList();
+ X509Certificate leafCert = TestData.LEAF_CERT_1;
+
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ CertUtils.buildCertPath(
+ CertUtils.buildPkixParams(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert)));
+ }
+
+ @Test
+ public void validateCert_succeeds() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_2;
+ CertUtils.validateCert(TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert);
+ }
+
+ @Test
+ public void validateCert_throwsIfExpired() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_TRUSTED;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_2;
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ CertUtils.validateCert(
+ TestData.DATE_LEAF_CERT_2_EXPIRED, rootCert, intermediateCerts,
+ leafCert));
+ }
+
+ @Test
+ public void validateCert_throwsIfWrongRootWithTheSameCommonName() throws Exception {
+ X509Certificate rootCert = TestData.ROOT_CA_DIFFERENT_KEY;
+ List<X509Certificate> intermediateCerts =
+ Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2);
+ X509Certificate leafCert = TestData.LEAF_CERT_2;
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ CertUtils.validateCert(
+ TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts,
+ leafCert));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
new file mode 100644
index 0000000..52269d9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class CertXmlTest {
+
+ private byte[] certXmlBytes;
+
+ @Before
+ public void setUp() throws Exception {
+ certXmlBytes = TestData.readTestFile("xml/valid-cert-file.xml");
+ }
+
+ @Test
+ public void parse_succeeds() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ assertThat(certXml.getSerial()).isEqualTo(1000L);
+ assertThat(certXml.getRefreshInterval()).isEqualTo(2592000L);
+ }
+
+ @Test
+ public void parse_succeedsIfNoIntermediateCerts() throws Exception {
+ CertXml certXml =
+ CertXml.parse(TestData.readTestFile("xml/valid-cert-file-no-intermediates.xml"));
+ assertThat(certXml.getAllIntermediateCerts()).isEmpty();
+ }
+
+ @Test
+ public void parse_checkIntermediateCerts() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ List<X509Certificate> intermediateCerts = certXml.getAllIntermediateCerts();
+ assertThat(intermediateCerts)
+ .containsExactly(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2)
+ .inOrder();
+ }
+
+ @Test
+ public void parse_checkEndpointCerts() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ List<X509Certificate> endpointCerts = certXml.getAllEndpointCerts();
+ assertThat(endpointCerts).hasSize(3);
+ assertThat(endpointCerts).containsAllOf(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
+ }
+
+ @Test
+ public void parse_throwsIfNoEndpointCert() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-cert-file-no-endpoint-cert.xml")));
+ assertThat(expected.getMessage()).contains("at least one");
+ }
+
+ @Test
+ public void parse_throwsIfNoRefreshInterval() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-cert-file-no-refresh-interval.xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+
+ @Test
+ public void parse_throwsIfNoSerial() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-cert-file-no-serial.xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+
+ @Test
+ public void parse_throwsIfTwoRefreshIntervals() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-cert-file-two-refresh-intervals"
+ + ".xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+
+ @Test
+ public void parse_throwsIfTwoSerials() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ CertXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-cert-file-two-serials.xml")));
+ assertThat(expected.getMessage()).contains("exactly one node");
+ }
+
+ @Test
+ public void parseAndValidateAllCerts_succeeds() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ for (int index = 0; index < certXml.getAllEndpointCerts().size(); index++) {
+ assertThat(
+ certXml.getEndpointCert(
+ index, TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_TRUSTED))
+ .isNotNull();
+ }
+ }
+
+ @Test
+ public void parseAndValidate_returnsExpectedCertPath() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ CertPath certPath =
+ certXml.getEndpointCert(
+ /*index=*/ 1, // TestData.LEAF_CERT_2
+ TestData.DATE_ALL_CERTS_VALID,
+ TestData.ROOT_CA_TRUSTED);
+ assertThat(certPath.getCertificates())
+ .containsExactly(
+ TestData.LEAF_CERT_2, TestData.INTERMEDIATE_CA_2,
+ TestData.INTERMEDIATE_CA_1)
+ .inOrder();
+ }
+
+ @Test
+ public void validateCert_throwsIfRootCertWithDifferentCommonName() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ certXml.getEndpointCert(
+ /*index=*/ 0, // TestData.LEAF_CERT_1
+ TestData.DATE_ALL_CERTS_VALID,
+ TestData.ROOT_CA_DIFFERENT_COMMON_NAME));
+ }
+
+ @Test
+ public void validateCert_throwsIfRootCertWithDifferentPublicKey() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ certXml.getEndpointCert(
+ /*index=*/ 0, // TestData.LEAF_CERT_1
+ TestData.DATE_ALL_CERTS_VALID,
+ TestData.ROOT_CA_DIFFERENT_KEY));
+ }
+
+ @Test
+ public void validateCert_throwsIfExpired() throws Exception {
+ CertXml certXml = CertXml.parse(certXmlBytes);
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ certXml.getEndpointCert(
+ /*index=*/ 1, // TestData.LEAF_CERT_2
+ TestData.DATE_LEAF_CERT_2_EXPIRED,
+ TestData.ROOT_CA_TRUSTED));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/SigXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/SigXmlTest.java
new file mode 100644
index 0000000..4d87006
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/SigXmlTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class SigXmlTest {
+
+ private byte[] certXmlBytes;
+ private byte[] sigXmlBytes;
+
+ @Before
+ public void setUp() throws Exception {
+ certXmlBytes = TestData.readTestFile("xml/valid-cert-file.xml");
+ sigXmlBytes = TestData.readTestFile("xml/valid-sig-file.sig.xml");
+ }
+
+ @Test
+ public void parseAndVerifyFileSignature_succeeds() throws Exception {
+ SigXml sigXml = SigXml.parse(sigXmlBytes);
+ sigXml.verifyFileSignature(
+ TestData.ROOT_CA_TRUSTED, certXmlBytes, TestData.DATE_ALL_CERTS_VALID);
+ }
+
+ @Test
+ public void parseAndVerifyFileSignature_throwsIfExpiredCert() throws Exception {
+ SigXml sigXml = SigXml.parse(sigXmlBytes);
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ sigXml.verifyFileSignature(
+ TestData.ROOT_CA_TRUSTED, certXmlBytes,
+ TestData.DATE_INTERMEDIATE_CA_2_EXPIRED));
+ }
+
+ @Test
+ public void parseAndVerifyFileSignature_throwsIfInvalidSignature() throws Exception {
+ SigXml sigXml = SigXml.parse(sigXmlBytes);
+ byte[] modifiedBytes = sigXmlBytes.clone();
+ modifiedBytes[0] ^= (byte) 1; // Flip one bit
+ CertValidationException expected =
+ expectThrows(
+ CertValidationException.class,
+ () ->
+ sigXml.verifyFileSignature(
+ TestData.ROOT_CA_TRUSTED, modifiedBytes,
+ TestData.DATE_ALL_CERTS_VALID));
+ assertThat(expected.getMessage()).contains("signature is invalid");
+ }
+
+ @Test
+ public void parseAndVerifyFileSignature_throwsIfRootCertWithWrongCommonName() throws Exception {
+ SigXml sigXml = SigXml.parse(sigXmlBytes);
+ assertThrows(
+ CertValidationException.class,
+ () ->
+ sigXml.verifyFileSignature(
+ TestData.ROOT_CA_DIFFERENT_COMMON_NAME,
+ certXmlBytes,
+ TestData.DATE_ALL_CERTS_VALID));
+ }
+
+ @Test
+ public void parse_succeedsWithoutIntermediateCerts() throws Exception {
+ SigXml.parse(TestData.readTestFile("xml/valid-sig-file-no-intermediates.sig.xml"));
+ }
+
+ @Test
+ public void parse_throwsIfNoSignerCert() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ SigXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-sig-file-no-signer-cert.sig.xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+
+ @Test
+ public void parse_throwsIfTwoSignerCerts() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ SigXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-sig-file-two-signer-certs.sig.xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+
+ @Test
+ public void parse_throwsIfNoSignatureValue() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ SigXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-sig-file-no-signature.sig.xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+
+ @Test
+ public void parse_throwsIfTwoSignatureValues() throws Exception {
+ CertParsingException expected =
+ expectThrows(
+ CertParsingException.class,
+ () ->
+ SigXml.parse(
+ TestData.readTestFile(
+ "xml/invalid-sig-file-two-signatures.sig.xml")));
+ assertThat(expected.getMessage()).contains("exactly one");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/TestData.java
new file mode 100644
index 0000000..5eb4166
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/TestData.java
@@ -0,0 +1,307 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.certificate;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+
+import com.google.common.io.ByteStreams;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.Date;
+
+/** Constants used by the tests in the folder. */
+public final class TestData {
+
+ private static final String TEST_FILE_FOLDER_NAME = "KeyStoreRecoveryControllerTest";
+
+ private TestData() {}
+
+ // Some test data that is generated by using OpenSSL command line tools.
+ private static final String ROOT_CA_TRUSTED_BASE64 = ""
+ + "MIIJJzCCBQ6gAwIBAgIJAM7fBGeQ1wBkMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
+ + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAxMTEwNjQ5MzNaFw0zODAx"
+ + "MDYwNjQ5MzNaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCBCIw"
+ + "DQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBCcFv05M1seLPYIW7oFivh7u5otCt"
+ + "Mm7ryq0UjpbTQPcQxJQbYzcUQF7CbPYTdWDid4EuO8Zec03ownsduFhKud6H3yIQ"
+ + "4ueGiqCBoG1D6+N8fF9R8awTmAsbNg63VInx6IwcBZnjFlsIIftOFxqIJBYhiKhN"
+ + "JhPtF1i9dn+N5HjEKmkJO3pXhYhRXMp7OwL/epxzhBXFYT7aDg9pnaM6C+4hmhQ/"
+ + "0q2oyzYtAqFmrEenbtI6G47SzMc+shNTuYLtq21j/Z3uA3RwB9Szfu99F66tlgTX"
+ + "v7K7YS573hN3TQY/+nkLfFy/oF2LQRYvKHF+Nv0BHzQLzqDEYBaILcMf3i2Ce/b7"
+ + "wZjitLqFAI1swqGzgH/QpB3OrX51M/B7UCF2nB7Pa8knu4kBDGkz2Q41jAL0W/qt"
+ + "j43VwJDW0Y98OuqQiCqJrTrGdv7b/phnVVBvFrtIjYMfyK34jy5VLXctV5CSkWj5"
+ + "3ul3mvGFHJD+6nneDR4PUkmYN0khT4t/RqnQlwYE0a6Erq1+Rof6/DoWSzeBLBYV"
+ + "JaHhRy9mrudR/VcQynLKty6Zst4Lyh6aPMHcpTwGZbG+4mXnWeTaLEnGvivldksT"
+ + "XOxipcO/fXJfDss4b0glGzP3GD0+H5EZB9coYzNT47QZd9drxHdrLxtPoi+MeqkG"
+ + "gCdyFyBZO8G2k/JuyziT6hy+50VXJnl6Ujxj7MVUYAsISHsHgqETDsukQbbKvTKg"
+ + "3gxPVNN/vKWwyh7KLcFIaOEoPOgStkmVsqrXm7YLE6Bvzm8nu4rwJeAF9Yseg9BE"
+ + "Y86TRRmAI7fW4eDEPnxgCUUvcYSAh5mcayIyIr0KTuXkevwYbVRHMVmy9DaqzsP8"
+ + "YFXIqFvDXRCFSy/gMkoNb9ZoqdkmjZ+VBsjAKI+u/Haf6pgdpGZbVGKEFmaVHCkr"
+ + "tPp/gy4kE4qmd/SIaccG8o6Eb9X9fbqTTDZv34kcGgxOvBJVIaNHprTjgvYEnRaD"
+ + "KTlmZoCUmBlHzvbf68YWBmIz0K8vYPdx9r98LiUgpbTHtKZIYrJnbgPnbC9icP24"
+ + "2ksB4yaTx1QWc14vTNv1lUtv4zJEmaaoynNlETJFf/Tz0QKJxtT+l/BIAz8kEJMA"
+ + "cKsfoTx9OTtfuL85pXbCgxbKKmKn6RzxUCzSzgMboC0z6W8Zxy2gLIhqMm8AXAF7"
+ + "salwrRirV4lWsM9MOhVEgfjcv/qmQSYr1ARrwwegHRqxPA3qh11kfq5YSFU7W7+f"
+ + "JrWH6VuLZ0B1fj2+lsoMNekFA1ULD8DK7aAFIh9Y1y4Jt//xMuOPcD5PWNGFmUk7"
+ + "oPewiIUMLjXSWcgrQVYbZEDW/vooMJoo47Vg1fQPehejbONE1nBIaeRVhJcCAwEA"
+ + "AaNjMGEwHQYDVR0OBBYEFNd7oYeSi7hSGimRpTZaHLQy6+zRMB8GA1UdIwQYMBaA"
+ + "FNd7oYeSi7hSGimRpTZaHLQy6+zRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/"
+ + "BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IEAgABny011veuPplTcNeyLeKhWRn/y9VM"
+ + "QEyhaLlsC1Di35WN8owjj4+gugLlDdtEc/pvzgk+ZkAuplQkrsU907dvwkDPb4rW"
+ + "ZB5hjbr9yVyEHK1bHh7RSUkJrB3NRwqH1lyWz/LfaVfbV4CtaaERhThzZCp/MweO"
+ + "Tivg2XpSluy5s8lEEbMC/dGuIsBMitX3XLlbYa2ND3aHZLo6X9yQMFfTCjgwYG2n"
+ + "eDYupnvcLINlrlJYcrYSrIvoQn9XfsnjU3AXz+jc6xLrO3EtXDhi9e+QTfcnvRsg"
+ + "l/Hj9SZr1w1L1PPJo+KjsRavVvzaHXlBAvvUtEojJrkR3j+b5zvQB6dgVrM0zUhM"
+ + "Q9zRp5R/xqHeZ/0TTQe9kEa8QuRzuRIkK5Wbh76Eix3S+2uTsbj462nk4E0oPR8p"
+ + "iYopS4ZEFEfrKW14HOph9ZscI4l/HfDmTNfgpyFl62UrvzVBnoz+sbhTgbPHPcCX"
+ + "OUrhmpz9I5oBkyEAZYunSvzY/9SXUsz6psXHJmVzLQcne/YQTtpWzV/wGD7cjjDl"
+ + "bfzsmGCfZ8jqPBoicl5IUVdyZsJgufEZHXxKZQ7wL7R6jKrj/GtCDey1Wr2QT8VX"
+ + "5JTk9cJQFjgjDWaAyCBpGEaQvYJcaOxk2D+Wap5ax8nUfW/99vVFA0EJKsSVVzw7"
+ + "daRty0UpfZsx2Sfzpg0mymmgB8+NY6t68dL5C/xxAv5mEQ8wGJmP45iQpo5T6LVV"
+ + "MrktLf5eIzxlALQIW/AgpSH9JKCqpItdxfisAIIs9e8XHbVJJA0Jde7rtAj+TUY0"
+ + "h00xSqyfSSbpcDJ9lIoSZOJvFQdWOxB8c3vZZGGhMuRFm06sUHvcHjo8KwnbqyOx"
+ + "DGjeqt6YWty6WcNin0WciR33vGHIzwVNxNnmuY308bNsMvY9jsmd37hdmmwnmQge"
+ + "7AIa7TMPjaKm0vV/1ztFSODWCI2K7klmL2MtOJMGfqUeOfjPANbS3lMJBAH9qxLM"
+ + "7Kng+nfqVtt+NG9MxcTbP80FkBa/6JxGgjjsiwDmhr2MTCYOK/eD+WZikMOieyvH"
+ + "m2vgxYCdWrhaGfc3t6oQ2YO+mXI7e6d3F3a90UUYkBIgje9zu0RLxnBBhuoRyGwv"
+ + "uQAlqgMDBZIzTO0Vnwew7KRLdzLhWbiikhi81q6Lg62aWjbdF6Ue6AVXch+dqmr+"
+ + "9aVt0Y6ETTS77nrQyglyLKIeNx6cEHDjETXlPYGbCAlrdKAdTA4ngnBZnzGQ/8zg"
+ + "tP9zvIJVA6cuOAn8GFEsrb7GN20QSDwyJWrYi6f+m64D9rOK4Jz4t+lEfjcfJeM/"
+ + "UcNlhmATcMHXWPCoKkOfll4PBc/Wigv1xYw70RZ4pai07LzJxNHYhvpE3Q==";
+ private static final String ROOT_CA_DIFFERENT_KEY_BASE64 = ""
+ + "MIIFJjCCAw6gAwIBAgIJANpazyIWdcb/MA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNV"
+ + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAxMjEwMzI0MjVaFw0zODAx"
+ + "MTYwMzI0MjVaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
+ + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKkAUqkRxdVy9UpI9BjQnylGVPRW"
+ + "aQsCwT2iWJ7fuCnQQon1U9nOyw2R5GYKcA8Zy+4Co++6nzRblYYXJG3Fzsj+kxei"
+ + "pZGmU11djRJDOhHPPe5jSW37Y0czWaj8jx4xvMim18dGYR7fg6SsOOYXA2y5tlvZ"
+ + "xLjvw5qpL62J5bVoAAxjng/Oc2Osu+vpv6M50pUZr0OEiFi59WlwrCCZpf1/80bT"
+ + "j2ebCKKAtTYa6+Q+oMGGxb3imSRpmQPtFcvhUPmAaocUjYM/FeIGNRv14oED/aXz"
+ + "khuPb+QNkXgk9yiokE10IeAk6oNUNDyuiMNIFy67lUrwc45lv9y0s/8fHj9pvKse"
+ + "n3+UOKAuV9atUXLdFKQwnQPt4SOmHPkXoj+5tv32RSvVeYhb0ZOpQPkRhxv4wgs7"
+ + "NldNbKhzVDM9K4M5Q2TrPK1yJKrc5/z0bDzmPOcH4AAXPvSt5PZOs0NlXUJ99BcA"
+ + "KE1sWArUhipz5mx0hxPTNEM9/8bMb//HkbZtx2log1/fc207W/AFd2FICOpRY+Sb"
+ + "CJs9WjstpisppotONvgXxZbZiypGKxpeZOb4s6y3iXtZ0FWXUZrc65b8S06fDVCa"
+ + "ZomNFDWhspHFKyueBgU6cR9K97cDo85Juy6RhnouXxi+XpXPdGFwPqVm1glFYos8"
+ + "5+Scbwwx69RihN1nAgMBAAGjYzBhMB0GA1UdDgQWBBRoosUAVtfHHeMl8/5x1m2m"
+ + "arEOoTAfBgNVHSMEGDAWgBRoosUAVtfHHeMl8/5x1m2marEOoTAPBgNVHRMBAf8E"
+ + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAYbLVQlsi"
+ + "KdzCPsaybSrUtMrmkad8Gy+/QR3J16adxy/2WpxsZ1Su0YA4tFzOSWqt74C0mnUi"
+ + "i+3prW7nyEOwezs+NH0SreF4B7tO1FSj157LoHxR2WmCCwul8FfsMi0x6MvEf40v"
+ + "bMeLrRAA4ysRITua1INb05fzJpczoaW/Q1lBPXConvolnIMvYsLbCZX4/OhQQCLa"
+ + "mJx8mGrt1wcNp7Kvh+3JfuOXw+WGeCuB5sSWBsOUvhfN+8sgyk1Dtq5c7rVKKtqz"
+ + "gqHfCNZ+lYa6Vkii7plIcWYJXXa7DMmozX07mrDqdJZEocv6XCoZAKDlJorB8pQT"
+ + "im47RF60X/FCHSCK2cHbG5M+kF84xwj5fTLztLM1+RlJSJiGk6jhxJRQ+hl0Vkhp"
+ + "+u7UbUDwkUF/CJB3d1Gtfm+QtzFVKe27ClU5YFSKCXRV/K4KnkZqpyG8Py+PUtUf"
+ + "WRahp4hkWFkIoLeTnJwgAFRvp/KCtSW0/trI/vfInzqBk/qWIVhxYB8Qv9DXtKBX"
+ + "3AZ36HM2dxmjef/rpYRphuEN0ZwYdynsGy9dF0SihbR8curSg67sbtYfyw0xURhU"
+ + "Nk99YMy7T0EUYnaki/PIPK/gnJjZTX1FLCyHUR38fDJIWkGB4xr8pSrwmRoPSvNF"
+ + "dFr3YlFWwHWd1gXlwJtzeMSuVgoKVtZGmmk=";
+ private static final String ROOT_CA_DIFFERENT_COMMON_NAME_BASE64 = ""
+ + "MIIJFzCCBP6gAwIBAgIJAMobGgw5LXwqMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV"
+ + "BAMMDVdyb25nIFJvb3QgQ0EwHhcNMTgwMTE2MTgzNDA5WhcNMzgwMTExMTgzNDA5"
+ + "WjAYMRYwFAYDVQQDDA1Xcm9uZyBSb290IENBMIIEIjANBgkqhkiG9w0BAQEFAAOC"
+ + "BA8AMIIECgKCBAEJwW/TkzWx4s9ghbugWK+Hu7mi0K0ybuvKrRSOltNA9xDElBtj"
+ + "NxRAXsJs9hN1YOJ3gS47xl5zTejCex24WEq53offIhDi54aKoIGgbUPr43x8X1Hx"
+ + "rBOYCxs2DrdUifHojBwFmeMWWwgh+04XGogkFiGIqE0mE+0XWL12f43keMQqaQk7"
+ + "eleFiFFcyns7Av96nHOEFcVhPtoOD2mdozoL7iGaFD/SrajLNi0CoWasR6du0job"
+ + "jtLMxz6yE1O5gu2rbWP9ne4DdHAH1LN+730Xrq2WBNe/srthLnveE3dNBj/6eQt8"
+ + "XL+gXYtBFi8ocX42/QEfNAvOoMRgFogtwx/eLYJ79vvBmOK0uoUAjWzCobOAf9Ck"
+ + "Hc6tfnUz8HtQIXacHs9rySe7iQEMaTPZDjWMAvRb+q2PjdXAkNbRj3w66pCIKomt"
+ + "OsZ2/tv+mGdVUG8Wu0iNgx/IrfiPLlUtdy1XkJKRaPne6Xea8YUckP7qed4NHg9S"
+ + "SZg3SSFPi39GqdCXBgTRroSurX5Gh/r8OhZLN4EsFhUloeFHL2au51H9VxDKcsq3"
+ + "Lpmy3gvKHpo8wdylPAZlsb7iZedZ5NosSca+K+V2SxNc7GKlw799cl8OyzhvSCUb"
+ + "M/cYPT4fkRkH1yhjM1PjtBl312vEd2svG0+iL4x6qQaAJ3IXIFk7wbaT8m7LOJPq"
+ + "HL7nRVcmeXpSPGPsxVRgCwhIeweCoRMOy6RBtsq9MqDeDE9U03+8pbDKHsotwUho"
+ + "4Sg86BK2SZWyqtebtgsToG/Obye7ivAl4AX1ix6D0ERjzpNFGYAjt9bh4MQ+fGAJ"
+ + "RS9xhICHmZxrIjIivQpO5eR6/BhtVEcxWbL0NqrOw/xgVcioW8NdEIVLL+AySg1v"
+ + "1mip2SaNn5UGyMAoj678dp/qmB2kZltUYoQWZpUcKSu0+n+DLiQTiqZ39Ihpxwby"
+ + "joRv1f19upNMNm/fiRwaDE68ElUho0emtOOC9gSdFoMpOWZmgJSYGUfO9t/rxhYG"
+ + "YjPQry9g93H2v3wuJSCltMe0pkhismduA+dsL2Jw/bjaSwHjJpPHVBZzXi9M2/WV"
+ + "S2/jMkSZpqjKc2URMkV/9PPRAonG1P6X8EgDPyQQkwBwqx+hPH05O1+4vzmldsKD"
+ + "FsoqYqfpHPFQLNLOAxugLTPpbxnHLaAsiGoybwBcAXuxqXCtGKtXiVawz0w6FUSB"
+ + "+Ny/+qZBJivUBGvDB6AdGrE8DeqHXWR+rlhIVTtbv58mtYfpW4tnQHV+Pb6Wygw1"
+ + "6QUDVQsPwMrtoAUiH1jXLgm3//Ey449wPk9Y0YWZSTug97CIhQwuNdJZyCtBVhtk"
+ + "QNb++igwmijjtWDV9A96F6Ns40TWcEhp5FWElwIDAQABo2MwYTAdBgNVHQ4EFgQU"
+ + "13uhh5KLuFIaKZGlNloctDLr7NEwHwYDVR0jBBgwFoAU13uhh5KLuFIaKZGlNloc"
+ + "tDLr7NEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN"
+ + "AQELBQADggQCAAWZS0RIH4FdQhlUkdJW06cUYYIJ7p8rRI+6XgMkfj7vupjVnfDN"
+ + "3Z3s0yf+dGaGGJbv68W952j33z94FuubsacCixNSaS4bIwLXvcL9kQtI9Qf09Pd6"
+ + "GB102Q6nz/daLVF/5urQPbp4UFkyhcP/d2mg9CmxwBL3Djrf/dKf4EoiV5xmQpUB"
+ + "a6BNBMntnK1VZ3y8YxDF3XnhbjUZAOTLLYe4+isqvXPb9osdmdaZQU3iHQQwGYJN"
+ + "6rTYvHmsdfU5eLYLdWxOOm/5Sz5rWXxa1XqSfQgOIaFYQ1w69Z+3BfNbaBnYck3l"
+ + "xtxGHromigt+2iimXFFML7EHiSznVhHl3SOX0RBLeUvP8oNwwSsaHXuXbceYWvb+"
+ + "ic7FyTN4f3+wRGjN01U3be93dj+qZlvTmCzpOrJeUcym3F94d0tWQvk3kkBp/Egi"
+ + "Dd85vYWCdEeCfODW6sReVdj/IuT5xv1T8kKaNaEjJjAjeX6xjPskw3I2LuPeEyz+"
+ + "26LiOs1hPRHC4CL3JS6LNmRmIYKuy7K0DHwxS6wplDYXXH+a0VuLvQrbsw5zTh3f"
+ + "Xwq0CLGwPPRyMnFk13+PYBa0bmJ1dNu5hUc9biziCJlvcg0c+FzzUYG4poN0R73R"
+ + "XPyFHmpULAHit05dw3QaPZqX1GCeiVxrCl6N6G4/9PsVOvi/WEUasHDkk+R4/r9b"
+ + "RwvQw0PVdDvGndouRcSzHvPEdW9y2TxSDhltvtC2xvp3mGaT0j2+cCRDINLFB6rK"
+ + "v18oLzpzu3HaZ2ptmm4OpeRnCTLa4qheV1rlSWi3mvPoh77glgHEyTHvhiFrlq+6"
+ + "f6oMpkcbp4KtOT/npvB3yY4RZWn+J1cDcOW34ssSN2PDVSx1IsianxtSCXKReYrv"
+ + "kjS4sTQWw0LYG6146g2rz1OKzuT+6YSIPlpw/4/DK0Sz/Q1AY0cuOHhgMSW0cSaE"
+ + "6PQ275hFjJ4zYYEYNOp56nhsvgbLAu1V5rwQpwi2RNo4teFzP/AKyZzNbApfl8Q5"
+ + "PKHHE8+Uk3/oLZ8h12JzceKL5ivXU4i8r9sw+o7b/UReVsbrFDXTuO3sRFyA5kI0"
+ + "aBpYrAyb8xVubbi7gCWhDfJsSKc7CR2N+EUtBrT08TD0AtuxpRLr6Sz2RrTabZNV"
+ + "lQlgWmKalWT/i7gyA/MNxbBK0hyr7Pvl5f7Ud2+muKOfhEXiCzp8yzXU9I8cTz2p"
+ + "K85LYJfRNIO2kPrNLdomPL+J2S298GvX2j08CZR8qBXLOs6XJOvZ1KC8BrJ9M7kG"
+ + "2MHGXeL+9/11khM09dbC+Q0NUKTKOpSU7M7RaON1Dp4RyzdIcdwZ/dFvvxcYbtOP"
+ + "6OAzRvlk6CZWG3obkt3yaB1NhBEw8hiYvX9F";
+ private static final String INTERMEDIATE_CA_1_BASE64 = ""
+ + "MIIHMDCCAxegAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVR29v"
+ + "Z2xlIENyeXB0QXV0aFZhdWx0MB4XDTE4MDExMjAwMjM1N1oXDTI4MDExMDAwMjM1"
+ + "N1owLTErMCkGA1UEAwwiR29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0"
+ + "ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALlhoKYuwxaatY9ERDFp"
+ + "iEygSnjy0xzaiF4uCyiTfAuboSi5QwGon3ohf0ufJF02L9lnTMoeBAg+88m8AMgW"
+ + "KFcEupabqlZfA3F/50mMCmnJvBSLXJ+chUdcAVpwcZAsq6ko22ARBxao1wu2qxNe"
+ + "D8eXiiK8DRpTtKy3wOldZJ222v35v9JGOTjORZRrcv7Z8f6I5/cSsTS+WoVk/aBt"
+ + "QqyFkcdw1zqulnlFE2rxNAVgyLlW71WJikYTDtUDeo79LvkDGjVsLo2MfpNxuK+5"
+ + "MuMqzyN5LmzXJmNCEW1O5IIIUAPhgy5s08G+3G644wCEWsAnv2FBWLBn/HmJu6Uq"
+ + "nSM2AaJN56V0tJG/yL2VgTnPJrJypNTKZW3OTCLCaYcTEbKfarxLwVWxvIWSIgkn"
+ + "0q57GYhf7O+x9vvcOUmZwVxZECorIiK4n5AWG/KD3dWI3UGGGpYsDazRngA/bQPu"
+ + "DSzBP9FBVcQt3/DMBG1s6f2Eko5f6aTFcVW9iV7aWLeIq+pQYlbmG42decj+aHLQ"
+ + "COp5KV+Q77y4kFhZQFAQ1mN4crnhuEc1K5SmjAK24zIqWbwM3ly0KSQFc9jAmONg"
+ + "0xu7kAObP3PZk85En12yLLscNmHCWYfOOEvTHf4KX7tjBl4HHp/ur+2Qwgpt9MFB"
+ + "MGqR2cni5OV6gZcRdHaEerjrAgMBAAGjZjBkMB0GA1UdDgQWBBRE9RxHT7U3EP1v"
+ + "djRzNYMrU7EseDAfBgNVHSMEGDAWgBTXe6GHkou4UhopkaU2Why0Muvs0TASBgNV"
+ + "HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC"
+ + "BAIAAfa7FKkfh4t+G8Sv4n3gUcuEAtmpCsTtHJU67r5eSvR24fDX7sqqSIib9sQ8"
+ + "adA1FtnE3wnC4bEKnIwQLerWTN4i4V9oEKRIT5o5Rr61LyO9R+Prpo6uJVoPujFH"
+ + "GZPdIj/Qs4Xax4a11QD+70+ZmmdL6yfigVDWA4yduFdMg6heHRf3EFCbVBv5qbBS"
+ + "n8+eFKRnBZ/kQdFlYt+G+92Qqyn2uAcER6kZjIfPdnZh44SazLP37W8AkDX30Pmk"
+ + "V0PHVGCDrap44q3maI1m8NAE1jGwwmRzJL953x5XgbVGt0K/3cNoWtKLenwX/G3I"
+ + "akrgvOY5Zl0v3FRDZwGFt9UIBfZDDOGRMXIgIGs/1cvkwWpOT6dyReqDXempiQ1q"
+ + "Yy6J5VsK5WK6gEelUyoACbzgby25V6a79Q1MI7dXmFQfCcX0nAD/AZmM1HkeYgrC"
+ + "uq6fWoPOVMKML2mN90rCzkWxGaLcl5dPfad0O1LrcP48SRE5MXMWyxZZBon+wDIk"
+ + "ascyM/r4fmk4kq64YKdm2wxCDMNArAIcyBkwOaWWfabtSagxJ3qtMtxK0qBUsbLC"
+ + "yMyYpgU1h9c8rEdc4JgeE2LXJzxTKDc3SBOqbuNMlKWjYA+X+SUvVYALrQKAC+5v"
+ + "wdUhLYdAPAksqk/ZoiBjkW35FfvqQMJBY29VnDT1h7/Nxk5gu+goTA9oFIYNrNte"
+ + "+s0my+IUgYhKJBsgh7Mupv+B92GN5b3b440BMHB5QR959Jdq6BAXNUyZLM5fhZQE"
+ + "Jj/rxZFXaqq757kgUhwWBz5TDbYF7GkqTyM4k430xwJKY0AYYEHmv1UYNo5X4G3x"
+ + "SC2LhWC1b9VAykdkHbLs+IA8klxURmLmRiRj1UryhQjjT8h/FvNyPnbT1AKoElix"
+ + "QLnLi8thkJ+tQggO0hISFsIrKNfnn0V6O0VKw9UZsMigsbYG5EbzIXcAyy8Avr9n"
+ + "um7gBBZDt7fWso0+pG1UenJ+PybeuW/azQDLRw1Syz8OwU+ABRLq0JyyAtV7VPY5"
+ + "C9pkKS+bU8nECxr6dMhAbpLBHlKsyb1qtkOt1p7WagEQZFIIc6svc73+L/ET/lWn"
+ + "GBmkVVsCN7Aqyo5aXQWueXP4FUL+6O5+JALqw3qPeQgfnLkh0cUuccNND05QeEiv"
+ + "Zswc/23KJXy1XbdVKT3UP0RAF7DxstbRGQbAT3z+n931e3KhtU28OKjsFtoeq2Dj"
+ + "6STPEXh4rYFWMM8+DrJetAtBqk/i+vBwRA8f7jqIPPep/vEjPqqMOpdSVcoFQ1df"
+ + "JuOZtGfEUjFHnlDr3eGP7KUIEZvhan1zm544dDgPVTXxrY4moi2BhKEY69zRSX6B"
+ + "+a0fa5B3pxc8BN0LsHA0stT/Y2o=";
+ private static final String INTERMEDIATE_CA_2_BASE64 = ""
+ + "MIIESTCCAjGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwiR29v"
+ + "Z2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTIwMDM4MDNa"
+ + "Fw0yMzAxMTEwMDM4MDNaMDoxODA2BgNVBAMML0dvb2dsZSBDcnlwdEF1dGhWYXVs"
+ + "dCBJbnRlcm1lZGlhdGUgSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOC"
+ + "AQ8AMIIBCgKCAQEA0v3bN3MwKifDAkF1524XzuaxYtk1sQKUlAlNngh+Qv4RjCkX"
+ + "TECi7po8LeNsY+hWxmW3XZ22RBphe/yP4YcOdbqlIjZYNx3b75hCSJCadOkdW+Z9"
+ + "f6+tKsHgeUja6r9r2TThzileImAvjXShe7GZYW8csPv6HaEVRXQlu8fGAZf8skmJ"
+ + "EMfJx84//WeULdVz94beDhi9YAf4gLfmOayQcdWhDcMYI39knJcRny1ffRGgb1Hf"
+ + "lE+3/a3aGFeODaxfkPaGRxEhzhZ/JDBiNgUAH/u7C5nxqa2WOu5e0wq3S0TndIOE"
+ + "hmnwCE2GvxADFQst+rSsOn4EHit70hv4CfrMRQIDAQABo2YwZDAdBgNVHQ4EFgQU"
+ + "0dKv4xTaEmdwHyox3tY8H8XVDGIwHwYDVR0jBBgwFoAURPUcR0+1NxD9b3Y0czWD"
+ + "K1OxLHgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI"
+ + "hvcNAQELBQADggIBAJaArqLlJ2SLQ8JRwanD6LPlqQxucQ+x/LztTQpzPrsFfJds"
+ + "E/sDZr2rXhDpz/bifIdj/DCbQ6/3w+t5JdFjT8GjXLgz1kCa/4W409FgSTgy1ENn"
+ + "AMUU6pFIbOq/Qy/71uOTrrFWYTN5Vk+RBGxx5iDfHjDYraudi4VlcNkal4OyM98n"
+ + "N3qp9cZD0RtWxMhvq6ahgmf9cTbEw6+l8yf/bogGLBYXXYeOoO5Q134AxrrgfthE"
+ + "tvyKwJkT/l3OFKRcaHrebs+V1z5gPs7zWOyO5n2Z1SAmcOGfTfKMZWwp3Hi3OTr2"
+ + "gB3LUYKyQVhC70dka3X+IbnFg5YfzJtX6YGnHlnI1SufOkEpGQDfcc0UQAWg/lgb"
+ + "RkfMFD9tuJomBhyqv1YaxLN8yL4ZTRU0KCvvC5I5+X/zt9kBjnHlBOdYtknZT5jz"
+ + "7+mjqWdpmWoAjeV5+CgIzG2k7JAm6rQuE1ZQNF0wAYxPret4NHPJFqfD5gGhdrYw"
+ + "pEUxkcwHERA/E1CkpyqUy/Hd3kqHvnEDqzFcxBdUdmOgnbpI2nAZdEpfxmA5+M1n"
+ + "UoxQ8ZWAZH+Mdlkw/Hx5hVjGjz8snD4QN25pj/wT+V6AR5OmYb8yfsQb2S8a8yDp"
+ + "HzcIHW+dEWpX2boirOsrdI16kNtxYqtG7c5qWBPJy5Zjkvh9qbnfT/RQx10g";
+ private static final String LEAF_CERT_1_BASE64 = ""
+ + "MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi"
+ + "R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1"
+ + "NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW"
+ + "YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu"
+ + "tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl"
+ + "4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21"
+ + "tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu"
+ + "HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr"
+ + "GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb"
+ + "UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe"
+ + "33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5"
+ + "7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ"
+ + "hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa"
+ + "79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4"
+ + "M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf"
+ + "JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp"
+ + "BuwwuQxvQDF4pmQd";
+ private static final String LEAF_CERT_2_BASE64 = ""
+ + "MIICrDCCAZSgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwOjE4MDYGA1UEAwwvR29v"
+ + "Z2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZSBJbnRlcm1lZGlhdGUwHhcN"
+ + "MTgwMTEyMDEwMzA5WhcNMTkwMTEyMDEwMzA5WjArMSkwJwYDVQQDDCBHb29nbGUg"
+ + "Q3J5cHRBdXRoVmF1bHQgSW5zdGFuY2UgMjBZMBMGByqGSM49AgEGCCqGSM49AwEH"
+ + "A0IABGhmBQyWdjsXKJRbkW4iIrvt6iqhX5t2XGt/vZS9CoOl0fs+EvJXo4kgrnx8"
+ + "/8SGxz3pwRkFhY943QYy6a1gv/2jgZUwgZIwCQYDVR0TBAIwADAdBgNVHQ4EFgQU"
+ + "xFmLyxUS2JHKURBtewBKRP6kQBgwVgYDVR0jBE8wTYAU0dKv4xTaEmdwHyox3tY8"
+ + "H8XVDGKhMaQvMC0xKzApBgNVBAMMIkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnRl"
+ + "cm1lZGlhdGWCAhAAMA4GA1UdDwEB/wQEAwIDCDANBgkqhkiG9w0BAQsFAAOCAQEA"
+ + "EJWpl7HU6LxukLqhw2tVZr7IRrKIucRk+RKaaiMx1Hx2jsTTskiJRiZas/xoPSqX"
+ + "z1K5DVgI486i7HyqnWkGH5xVzCsv+rya5FOSTS3jVtgtoA4HFEqeeAcDowPDqVw3"
+ + "yFTA55ukZnzVaPLpDfPqkhzWiuLQ/4fI6YCmOnWB8KtHTMdyGsDSAkpoxpok++NJ"
+ + "Lu79BoBLe2ucjN383lTlieLxmrmHjF9ryYSQczcm0v6irMOMxEovw5iT4LHiEhbm"
+ + "DfOPW909fe/s+K3TGZ3Q6U77x8g5k9dVovMgA4pFwtREtknFjeK1wXR3/eXGcP3W"
+ + "0bMX1yTWYJQFWCG3DFoC5w==";
+
+ /** The cert of the root CA. */
+ static final X509Certificate ROOT_CA_TRUSTED = decodeBase64Cert(ROOT_CA_TRUSTED_BASE64);
+ /** This root CA cert has a different Common Name than ROOT_CA_TRUSTED. */
+ static final X509Certificate ROOT_CA_DIFFERENT_COMMON_NAME =
+ decodeBase64Cert(ROOT_CA_DIFFERENT_COMMON_NAME_BASE64);
+ /** This root CA cert has the same CN as ROOT_CA_TRUSTED, but a different public key. */
+ static final X509Certificate ROOT_CA_DIFFERENT_KEY =
+ decodeBase64Cert(ROOT_CA_DIFFERENT_KEY_BASE64);
+ /** This intermediate CA cert is signed by the corresponding private key of ROOT_CA_TRUSTED. */
+ static final X509Certificate INTERMEDIATE_CA_1 = decodeBase64Cert(INTERMEDIATE_CA_1_BASE64);
+ /** This intermediate CA cert is signed by the private key of INTERMEDIATE_CA_1. */
+ static final X509Certificate INTERMEDIATE_CA_2 = decodeBase64Cert(INTERMEDIATE_CA_2_BASE64);
+ /** This leaf cert is signed by the corresponding private key of INTERMEDIATE_CA_1. */
+ static final X509Certificate LEAF_CERT_1 = decodeBase64Cert(LEAF_CERT_1_BASE64);
+ /** This leaf cert is signed by the corresponding private key of INTERMEDIATE_CA_2. */
+ static final X509Certificate LEAF_CERT_2 = decodeBase64Cert(LEAF_CERT_2_BASE64);
+
+ private static X509Certificate decodeBase64Cert(String str) {
+ try {
+ byte[] bytes = Base64.getDecoder().decode(str);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ static final Date DATE_ALL_CERTS_VALID = new Date(1516406400000L); // Jan 20, 2018
+ static final Date DATE_LEAF_CERT_2_EXPIRED = new Date(1547254989001L); // Jan 12, 2019
+ static final Date DATE_INTERMEDIATE_CA_2_EXPIRED = new Date(1673397483001L); // Jan 11, 2023
+
+ static InputStream openTestFile(String relativePath) throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ return context.getResources().getAssets().open(TEST_FILE_FOLDER_NAME + "/" + relativePath);
+ }
+
+ static byte[] readTestFile(String relativePath) throws Exception {
+ InputStream in = openTestFile(relativePath);
+ return ByteStreams.toByteArray(in);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
new file mode 100644
index 0000000..37482a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.server.locksettings.recoverablekeystore.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecoverableKeyStoreDbHelperTest {
+
+ private static final long TEST_USER_ID = 10L;
+ private static final long TEST_UID = 60001L;
+ private static final String TEST_ALIAS = "test-alias";
+ private static final byte[] TEST_NONCE = "test-nonce".getBytes(UTF_8);
+ private static final byte[] TEST_WRAPPED_KEY = "test-wrapped-key".getBytes(UTF_8);
+ private static final long TEST_GENERATION_ID = 13L;
+ private static final long TEST_LAST_SYNCED_AT = 1517990732000L;
+ private static final int TEST_RECOVERY_STATUS = 3;
+ private static final int TEST_PLATFORM_KEY_GENERATION_ID = 11;
+ private static final int TEST_SNAPSHOT_VERSION = 31;
+ private static final int TEST_SHOULD_CREATE_SNAPSHOT = 1;
+ private static final byte[] TEST_PUBLIC_KEY = "test-public-key".getBytes(UTF_8);
+ private static final String TEST_SECRET_TYPES = "test-secret-types";
+ private static final long TEST_COUNTER_ID = -3981205205038476415L;
+ private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8);
+ private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
+ private static final long TEST_CERT_SERIAL = 1000L;
+
+ private static final String SQL_CREATE_V2_TABLE_KEYS =
+ "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
+ + KeysEntry._ID + " INTEGER PRIMARY KEY,"
+ + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_UID + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_ALIAS + " TEXT,"
+ + KeysEntry.COLUMN_NAME_NONCE + " BLOB,"
+ + KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
+ + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
+ + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
+ + KeysEntry.COLUMN_NAME_ALIAS + "))";
+
+ private static final String SQL_CREATE_V2_TABLE_USER_METADATA =
+ "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
+ + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
+ + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
+ + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
+
+ private static final String SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA =
+ "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
+ + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
+ + "UNIQUE("
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + ","
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
+
+ private SQLiteDatabase mDatabase;
+ private RecoverableKeyStoreDbHelper mDatabaseHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mDatabaseHelper = new RecoverableKeyStoreDbHelper(context);
+ mDatabase = SQLiteDatabase.create(null);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDatabase.close();
+ }
+
+ private void createV2Tables() throws Exception {
+ mDatabase.execSQL(SQL_CREATE_V2_TABLE_KEYS);
+ mDatabase.execSQL(SQL_CREATE_V2_TABLE_USER_METADATA);
+ mDatabase.execSQL(SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA);
+ }
+
+ @Test
+ public void onCreate() throws Exception {
+ mDatabaseHelper.onCreate(mDatabase);
+ checkAllColumns();
+ }
+
+ @Test
+ public void onUpgrade_beforeV2() throws Exception {
+ mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
+ RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ checkAllColumns();
+ }
+
+ @Test
+ public void onUpgrade_fromV2() throws Exception {
+ createV2Tables();
+ mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
+ RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ checkAllColumns();
+ }
+
+ private void checkAllColumns() throws Exception {
+ // Check the table containing encrypted application keys
+ ContentValues values = new ContentValues();
+ values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
+ values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
+ values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
+ values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
+ values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
+ values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
+ values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
+ assertThat(mDatabase.insert(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+ .isGreaterThan(-1L);
+
+ // Check the table about user metadata
+ values = new ContentValues();
+ values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID,
+ TEST_PLATFORM_KEY_GENERATION_ID);
+ assertThat(mDatabase.insert(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+ .isGreaterThan(-1L);
+
+ // Check the table about recovery service metadata
+ values = new ContentValues();
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_UID, TEST_UID);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION,
+ TEST_SNAPSHOT_VERSION);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT,
+ TEST_SHOULD_CREATE_SNAPSHOT);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS, TEST_SERVER_PARAMS);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
+ assertThat(
+ mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
+ values))
+ .isGreaterThan(-1L);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 097d214..1c5bcd4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -32,6 +32,8 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.TestData;
import com.android.server.locksettings.recoverablekeystore.WrappedKey;
import java.io.File;
@@ -370,6 +372,57 @@
pubkey);
}
+ public void setRecoveryServiceCertPath_replaceOldValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_2);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_2);
+ }
+
+ @Test
+ public void getRecoveryServiceCertPath_returnsNullIfNoValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
+ }
+
+ @Test
+ public void getRecoveryServiceCertPath_returnsInsertedValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_1);
+ }
+
+ @Test
+ public void setRecoveryServiceCertSerial_replaceOldValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+
+ mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1L);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 3L);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(3L);
+ }
+
+ @Test
+ public void getRecoveryServiceCertSerial_returnsNullIfNoValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
+ }
+
+ @Test
+ public void getRecoveryServiceCertSerial_returnsInsertedValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1234L);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
+ 1234L);
+ }
+
@Test
public void getRecoveryAgents_returnsUidIfSet() throws Exception {
int userId = 12;
@@ -493,17 +546,6 @@
}
@Test
- public void getRecoveryServicePublicKey_returnsFirstKey() throws Exception {
- int userId = 68;
- int uid = 12904;
- PublicKey publicKey = genRandomPublicKey();
-
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, publicKey);
-
- assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId)).isEqualTo(publicKey);
- }
-
- @Test
public void setServerParams_replaceOldValue() throws Exception {
int userId = 12;
int uid = 10009;
diff --git a/services/tests/servicestests/src/com/android/server/net/IpConfigStoreTest.java b/services/tests/servicestests/src/com/android/server/net/IpConfigStoreTest.java
new file mode 100644
index 0000000..9f4b754
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/IpConfigStoreTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.net.IpConfiguration;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.net.ProxyInfo;
+import android.net.StaticIpConfiguration;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit tests for {@link IpConfigStore}
+ */
+@RunWith(AndroidJUnit4.class)
+public class IpConfigStoreTest {
+
+ @Test
+ public void backwardCompatibility2to3() throws IOException {
+ final int KEY_CONFIG = 17;
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream outputStream = new DataOutputStream(byteStream);
+
+ IpConfiguration expectedConfig = new IpConfiguration(IpAssignment.DHCP,
+ ProxySettings.NONE, null, null);
+
+ // Emulate writing to old format.
+ writeDhcpConfigV2(outputStream, KEY_CONFIG, expectedConfig);
+
+ InputStream in = new ByteArrayInputStream(byteStream.toByteArray());
+ ArrayMap<String, IpConfiguration> configurations = IpConfigStore.readIpConfigurations(in);
+
+ assertNotNull(configurations);
+ assertEquals(1, configurations.size());
+ IpConfiguration actualConfig = configurations.get(String.valueOf(KEY_CONFIG));
+ assertNotNull(actualConfig);
+ assertEquals(expectedConfig, actualConfig);
+ }
+
+ @Test
+ public void staticIpMultiNetworks() throws Exception {
+ final String IFACE_1 = "eth0";
+ final String IFACE_2 = "eth1";
+ final String IP_ADDR_1 = "192.168.1.10/24";
+ final String IP_ADDR_2 = "192.168.1.20/24";
+ final String DNS_IP_ADDR_1 = "1.2.3.4";
+ final String DNS_IP_ADDR_2 = "5.6.7.8";
+
+ StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
+ staticIpConfiguration.ipAddress = new LinkAddress(IP_ADDR_1);
+ staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress(DNS_IP_ADDR_1));
+ staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress(DNS_IP_ADDR_2));
+
+ ProxyInfo proxyInfo = new ProxyInfo("10.10.10.10", 88, "host1,host2");
+
+ IpConfiguration expectedConfig1 = new IpConfiguration(IpAssignment.STATIC,
+ ProxySettings.STATIC, staticIpConfiguration, proxyInfo);
+ IpConfiguration expectedConfig2 = new IpConfiguration(expectedConfig1);
+ expectedConfig2.getStaticIpConfiguration().ipAddress = new LinkAddress(IP_ADDR_2);
+
+ ArrayMap<String, IpConfiguration> expectedNetworks = new ArrayMap<>();
+ expectedNetworks.put(IFACE_1, expectedConfig1);
+ expectedNetworks.put(IFACE_2, expectedConfig2);
+
+ MockedDelayedDiskWrite writer = new MockedDelayedDiskWrite();
+ IpConfigStore store = new IpConfigStore(writer);
+ store.writeIpConfigurations("file/path/not/used/", expectedNetworks);
+
+ InputStream in = new ByteArrayInputStream(writer.byteStream.toByteArray());
+ ArrayMap<String, IpConfiguration> actualNetworks = IpConfigStore.readIpConfigurations(in);
+ assertNotNull(actualNetworks);
+ assertEquals(2, actualNetworks.size());
+ assertEquals(expectedNetworks.get(IFACE_1), actualNetworks.get(IFACE_1));
+ assertEquals(expectedNetworks.get(IFACE_2), actualNetworks.get(IFACE_2));
+ }
+
+ // This is simplified snapshot of code that was used to store values in V2 format (key as int).
+ private static void writeDhcpConfigV2(DataOutputStream out, int configKey,
+ IpConfiguration config) throws IOException {
+ out.writeInt(2); // VERSION 2
+ switch (config.ipAssignment) {
+ case DHCP:
+ out.writeUTF("ipAssignment");
+ out.writeUTF(config.ipAssignment.toString());
+ break;
+ default:
+ fail("Not supported in test environment");
+ }
+
+ out.writeUTF("id");
+ out.writeInt(configKey);
+ out.writeUTF("eos");
+ }
+
+ /** Synchronously writes into given byte steam */
+ private static class MockedDelayedDiskWrite extends DelayedDiskWrite {
+ final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+
+ @Override
+ public void write(String filePath, Writer w) {
+ DataOutputStream outputStream = new DataOutputStream(byteStream);
+
+ try {
+ w.onWriteCalled(outputStream);
+ } catch (IOException e) {
+ fail();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/ReportUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/ReportEncoderTests.java
similarity index 63%
rename from services/tests/servicestests/src/com/android/server/net/watchlist/ReportUtilsTests.java
rename to services/tests/servicestests/src/com/android/server/net/watchlist/ReportEncoderTests.java
index 395969e..cb4f6c5 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/ReportUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/ReportEncoderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -16,15 +16,13 @@
package com.android.server.net.watchlist;
-import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.internal.util.HexDump;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,18 +35,21 @@
import java.io.InputStreamReader;
import java.util.HashMap;
+import com.android.service.nano.NetworkWatchlistReportProto;
+import com.android.service.nano.NetworkWatchlistAppResultProto;
+
/**
- * runtest frameworks-services -c com.android.server.net.watchlist.ReportUtilsTests
+ * runtest frameworks-services -c com.android.server.net.watchlist.ReportEncoderTests
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class ReportUtilsTests {
+public class ReportEncoderTests {
private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_config_test1.xml";
private static final String TEST_XML_1_HASH =
"C99F27A08B1FDB15B101098E12BB2A0AA0D474E23C50F24920A52AB2322BFD94";
- private static final String REPORT_HEADER_MAGIC = "8D370AAC";
- private static final String REPORT_HEADER_VERSION = "0001";
+ private static final int REPORT_VERSION = 1;
+ private static final int EXPECTED_REPORT_VERSION = 1;
private Context mContext;
private File mTestXmlFile;
@@ -56,7 +57,7 @@
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
- mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_config.xml");
+ mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_config.xml");
mTestXmlFile.delete();
}
@@ -67,26 +68,28 @@
@Test
public void testReportUtils_serializeReport() throws Exception {
- final byte[] expectedResult = HexDump.hexStringToByteArray(
- REPORT_HEADER_MAGIC + REPORT_HEADER_VERSION + TEST_XML_1_HASH
- + "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43" + "01"
- + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44" + "00"
- + "D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45" + "00"
- + "E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46" + "01"
- + "F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47" + "01"
- );
- HashMap<String, Boolean> input = new HashMap<>();
- input.put("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44", false);
- input.put("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", true);
- input.put("D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45", false);
- input.put("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46", true);
- input.put("F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47", true);
+ HashMap<String, Boolean> map = new HashMap<>();
+ map.put("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44", false);
+ map.put("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", true);
+ map.put("D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45", false);
+ map.put("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46", true);
+ map.put("F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47", true);
copyWatchlistConfigXml(mContext, TEST_XML_1, mTestXmlFile);
WatchlistConfig config = new WatchlistConfig(mTestXmlFile);
- byte[] result = ReportEncoder.serializeReport(config, input);
- assertArrayEquals(expectedResult, result);
+ final byte[] result = ReportEncoder.serializeReport(config, map);
+
+ // Parse result back to NetworkWatchlistReportDumpProto
+ final NetworkWatchlistReportProto reportProto =
+ NetworkWatchlistReportProto.parseFrom(result);
+ assertEquals(REPORT_VERSION, reportProto.reportVersion);
+ assertEquals(TEST_XML_1_HASH, reportProto.watchlistConfigHash);
+ assertEquals(map.size(), reportProto.appResult.length);
+ for (NetworkWatchlistAppResultProto appProto : reportProto.appResult) {
+ final String digest = appProto.appDigest;
+ assertEquals(map.get(digest), appProto.encodedResult);
+ }
}
private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 0abb48f..98483a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -294,7 +294,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
+ UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -328,7 +328,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
+ UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -561,34 +561,6 @@
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
}
- @Test
- public void testInsertPackageSetting() {
- final PackageSetting ps = createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
- final PackageParser.Package pkg = new PackageParser.Package(PACKAGE_NAME);
- pkg.applicationInfo.setCodePath(ps.codePathString);
- pkg.applicationInfo.setResourcePath(ps.resourcePathString);
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
- final Settings settings =
- new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
- pkg.usesStaticLibraries = new ArrayList<>(
- Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
- pkg.usesStaticLibrariesVersions = new long[] {2, 4, 6};
- settings.insertPackageSettingLPw(ps, pkg);
- assertEquals(pkg, ps.pkg);
- assertArrayEquals(pkg.usesStaticLibraries.toArray(new String[0]), ps.usesStaticLibraries);
- assertArrayEquals(pkg.usesStaticLibrariesVersions, ps.usesStaticLibrariesVersions);
-
- pkg.usesStaticLibraries = null;
- pkg.usesStaticLibrariesVersions = null;
- settings.insertPackageSettingLPw(ps, pkg);
- assertEquals(pkg, ps.pkg);
- assertNull("Actual: " + Arrays.toString(ps.usesStaticLibraries), ps.usesStaticLibraries);
- assertNull("Actual: " + Arrays.toString(ps.usesStaticLibrariesVersions),
- ps.usesStaticLibrariesVersions);
- }
-
private <T> void assertArrayEquals(T[] a, T[] b) {
assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
Arrays.equals(a, b));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 56d4b7e..857925b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -5149,7 +5149,8 @@
.forAllShortcuts(si -> {
switch (package1DisabledReason) {
case ShortcutInfo.DISABLED_REASON_VERSION_LOWER:
- assertEquals("This shortcut requires latest app",
+ assertEquals("App version downgraded, or isn’t compatible"
+ + " with this shortcut",
si.getDisabledMessage());
break;
case ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH:
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index c2072df..f559986a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -53,6 +53,7 @@
assertFalse(opt.isDowngrade());
assertFalse(opt.isForce());
assertFalse(opt.isDexoptIdleBackgroundJob());
+ assertFalse(opt.isDexoptInstallWithDexMetadata());
}
@Test
@@ -65,7 +66,8 @@
DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
DexoptOptions.DEXOPT_DOWNGRADE |
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
- DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+ DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -79,6 +81,7 @@
assertTrue(opt.isForce());
assertTrue(opt.isDexoptAsSharedLibrary());
assertTrue(opt.isDexoptIdleBackgroundJob());
+ assertTrue(opt.isDexoptInstallWithDexMetadata());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index c3714c8..f7516b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -63,6 +63,11 @@
return mBatteryLevel;
}
+ @Override
+ int injectBatteryPercent() {
+ return mBatteryLevel / 10;
+ }
+
void assertDumpable() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
dump(new PrintWriter(out), ""); // Just make sure it won't crash.
@@ -102,7 +107,7 @@
target.assertDumpable();
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.OFF,
@@ -110,7 +115,7 @@
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -118,7 +123,7 @@
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -126,7 +131,7 @@
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -134,7 +139,7 @@
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -142,7 +147,7 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -150,7 +155,7 @@
DozeState.LIGHT);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -158,7 +163,7 @@
DozeState.DEEP);
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.ON,
@@ -166,7 +171,7 @@
DozeState.NOT_DOZING);
target.advanceClock(1);
- target.drainBattery(3);
+ target.drainBattery(300);
target.transitionState(
BatterySaverState.OFF,
@@ -174,7 +179,7 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.ON,
@@ -182,12 +187,12 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.startCharging();
target.advanceClock(5);
- target.drainBattery(10);
+ target.drainBattery(1000);
target.transitionState(
BatterySaverState.ON,
@@ -195,25 +200,25 @@
DozeState.NOT_DOZING);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.startCharging();
target.assertDumpable();
assertEquals(
- "BS=0,I=0,D=0:{4m,10,150.00}\n" +
- "BS=1,I=0,D=0:{0m,0,0.00}\n" +
- "BS=0,I=1,D=0:{14m,8,34.29}\n" +
- "BS=1,I=1,D=0:{9m,9,60.00}\n" +
- "BS=0,I=0,D=1:{5m,1,12.00}\n" +
- "BS=1,I=0,D=1:{0m,0,0.00}\n" +
- "BS=0,I=1,D=1:{0m,0,0.00}\n" +
- "BS=1,I=1,D=1:{0m,0,0.00}\n" +
- "BS=0,I=0,D=2:{1m,2,120.00}\n" +
- "BS=1,I=0,D=2:{0m,0,0.00}\n" +
- "BS=0,I=1,D=2:{0m,0,0.00}\n" +
- "BS=1,I=1,D=2:{0m,0,0.00}",
+ "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
+ "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
+ "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
+ "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
+ "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
+ "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
target.toDebugString());
}
@@ -245,6 +250,7 @@
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
target.advanceClock(1);
@@ -277,15 +283,17 @@
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
target.advanceClock(10);
- target.drainBattery(10_000);
+ target.drainBattery(10000);
reset(mMetricsLogger);
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
target.advanceClock(1);
@@ -305,6 +313,7 @@
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 40964c0..78b6077 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -24,6 +24,7 @@
import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -35,6 +36,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
@@ -80,6 +82,8 @@
private static final String PACKAGE_1 = "com.example.foo";
private static final int UID_1 = 10000;
+ private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted";
+ private static final int UID_EXEMPTED_1 = 10001;
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
@@ -116,7 +120,7 @@
List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
- String mBoundWidgetPackage;
+ String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -223,10 +227,21 @@
pi.packageName = PACKAGE_1;
packages.add(pi);
+ PackageInfo pie = new PackageInfo();
+ pie.applicationInfo = new ApplicationInfo();
+ pie.applicationInfo.uid = UID_EXEMPTED_1;
+ pie.packageName = PACKAGE_EXEMPTED_1;
+ packages.add(pie);
+
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
try {
- doReturn(UID_1).when(mockPm).getPackageUidAsUser(anyString(), anyInt(), anyInt());
- doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(anyString(), anyInt());
+ doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
+ doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
+ anyInt(), anyInt());
+ doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
+ anyInt());
+ doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName),
+ anyInt());
} catch (PackageManager.NameNotFoundException nnfe) {}
}
@@ -239,14 +254,21 @@
private AppStandbyController setupController() throws Exception {
mInjector.mElapsedRealtime = 0;
+ setupPm(mInjector.getContext().getPackageManager());
AppStandbyController controller = new AppStandbyController(mInjector);
+ controller.initializeDefaultsForSystemApps(USER_ID);
controller.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mInjector.setDisplayOn(false);
mInjector.setDisplayOn(true);
setChargingState(controller, false);
- setupPm(mInjector.getContext().getPackageManager());
controller.checkIdleStates(USER_ID);
+ assertEquals(STANDBY_BUCKET_EXEMPTED,
+ controller.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID,
+ mInjector.mElapsedRealtime, false));
+ assertNotEquals(STANDBY_BUCKET_EXEMPTED,
+ controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
+ mInjector.mElapsedRealtime, false));
return controller;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 396fef4..57dd808 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -18,11 +18,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.graphics.Rect;
@@ -32,24 +30,17 @@
import android.view.SurfaceSession;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.stubbing.Answer;
/**
* Build/Install/Run:
* atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
*/
@Presubmit
-@Ignore("b/72450130")
@RunWith(AndroidJUnit4.class)
public class DimmerTests extends WindowTestsBase {
- public DimmerTests() {
- super(spy(new SurfaceAnimationRunner()));
- }
-
private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
final SurfaceControl mControl = mock(SurfaceControl.class);
final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
@@ -71,14 +62,11 @@
private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
final SurfaceSession mSession = new SurfaceSession();
+ final SurfaceControl mHostControl = mock(SurfaceControl.class);
final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
MockSurfaceBuildingContainer() {
super(sWm);
- mSurfaceControl = sWm.makeSurfaceBuilder(mSession)
- .setName("test surface")
- .setSize(1, 1)
- .build();
}
class MockSurfaceBuilder extends SurfaceControl.Builder {
@@ -88,34 +76,29 @@
@Override
public SurfaceControl build() {
- return spy(sWm.makeSurfaceBuilder(mSession)
- .setName("test surface")
- .setSize(1, 1)
- .build());
+ return mock(SurfaceControl.class);
}
}
@Override
- SurfaceControl.Builder makeSurface() {
- return sWm.makeSurfaceBuilder(mSession)
- .setName("test surface")
- .setSize(1, 1);
- }
-
- @Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
return new MockSurfaceBuilder(mSession);
}
@Override
+ public SurfaceControl getSurfaceControl() {
+ return mHostControl;
+ }
+
+ @Override
public SurfaceControl.Transaction getPendingTransaction() {
return mHostTransaction;
}
}
- MockSurfaceBuildingContainer mHost;
- Dimmer mDimmer;
- SurfaceControl.Transaction mTransaction;
+ private MockSurfaceBuildingContainer mHost;
+ private Dimmer mDimmer;
+ private SurfaceControl.Transaction mTransaction;
@Before
public void setUp() throws Exception {
@@ -123,13 +106,9 @@
mHost = new MockSurfaceBuildingContainer();
mTransaction = mock(SurfaceControl.Transaction.class);
- mDimmer = new Dimmer(mHost);
-
- doAnswer((Answer<Void>) invocation -> {
- Runnable runnable = invocation.getArgument(3);
- runnable.run();
- return null;
- }).when(sWm.mSurfaceAnimationRunner).startAnimation(any(), any(), any(), any());
+ mDimmer = new Dimmer(mHost,
+ (surfaceAnimator, t, anim, hidden) -> surfaceAnimator.mAnimationFinishedCallback
+ .run());
}
@Test
@@ -137,7 +116,7 @@
final float alpha = 0.8f;
mDimmer.dimAbove(mTransaction, alpha);
- SurfaceControl dimLayer = getDimLayer(null);
+ SurfaceControl dimLayer = getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -149,12 +128,12 @@
public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
float alpha = 0.8f;
mDimmer.dimAbove(mTransaction, alpha);
- final SurfaceControl firstSurface = getDimLayer(null);
+ final SurfaceControl firstSurface = getDimLayer();
alpha = 0.9f;
mDimmer.dimAbove(mTransaction, alpha);
- assertEquals(firstSurface, getDimLayer(null));
+ assertEquals(firstSurface, getDimLayer());
verify(mTransaction).setAlpha(firstSurface, 0.9f);
}
@@ -167,18 +146,18 @@
Rect bounds = new Rect(0, 0, width, height);
mDimmer.updateDims(mTransaction, bounds);
- verify(mTransaction).setSize(getDimLayer(null), width, height);
- verify(mTransaction).show(getDimLayer(null));
+ verify(mTransaction).setSize(getDimLayer(), width, height);
+ verify(mTransaction).show(getDimLayer());
}
@Test
public void testDimAboveNoChildNotReset() throws Exception {
mDimmer.dimAbove(mTransaction, 0.8f);
- SurfaceControl dimLayer = getDimLayer(null);
+ SurfaceControl dimLayer = getDimLayer();
mDimmer.resetDimStates();
mDimmer.updateDims(mTransaction, new Rect());
- verify(mTransaction).show(getDimLayer(null));
+ verify(mTransaction).show(getDimLayer());
verify(dimLayer, never()).destroy();
}
@@ -189,12 +168,12 @@
final float alpha = 0.8f;
mDimmer.dimAbove(mTransaction, child, alpha);
- SurfaceControl mDimLayer = getDimLayer(child);
+ SurfaceControl dimLayer = getDimLayer();
- assertNotNull("Dimmer should have created a surface", mDimLayer);
+ assertNotNull("Dimmer should have created a surface", dimLayer);
- verify(mTransaction).setAlpha(mDimLayer, alpha);
- verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, 1);
+ verify(mTransaction).setAlpha(dimLayer, alpha);
+ verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, 1);
}
@Test
@@ -204,12 +183,12 @@
final float alpha = 0.8f;
mDimmer.dimBelow(mTransaction, child, alpha);
- SurfaceControl mDimLayer = getDimLayer(child);
+ SurfaceControl dimLayer = getDimLayer();
- assertNotNull("Dimmer should have created a surface", mDimLayer);
+ assertNotNull("Dimmer should have created a surface", dimLayer);
- verify(mTransaction).setAlpha(mDimLayer, alpha);
- verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, -1);
+ verify(mTransaction).setAlpha(dimLayer, alpha);
+ verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, -1);
}
@Test
@@ -219,7 +198,7 @@
final float alpha = 0.8f;
mDimmer.dimAbove(mTransaction, child, alpha);
- SurfaceControl dimLayer = getDimLayer(child);
+ SurfaceControl dimLayer = getDimLayer();
mDimmer.resetDimStates();
mDimmer.updateDims(mTransaction, new Rect());
@@ -233,7 +212,7 @@
final float alpha = 0.8f;
mDimmer.dimAbove(mTransaction, child, alpha);
- SurfaceControl dimLayer = getDimLayer(child);
+ SurfaceControl dimLayer = getDimLayer();
mDimmer.resetDimStates();
mDimmer.dimAbove(mTransaction, child, alpha);
@@ -242,7 +221,29 @@
verify(dimLayer, never()).destroy();
}
- private SurfaceControl getDimLayer(WindowContainer windowContainer) {
- return mDimmer.mDimLayerUsers.get(windowContainer).mDimLayer;
+ @Test
+ public void testDimUpdateWhileDimming() throws Exception {
+ Rect bounds = new Rect();
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+
+ SurfaceControl dimLayer = getDimLayer();
+ bounds.set(0, 0, 10, 10);
+ mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction, times(1)).show(dimLayer);
+ verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
+ verify(mTransaction).setPosition(dimLayer, 0, 0);
+
+ bounds.set(10, 10, 30, 30);
+ mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
+ verify(mTransaction).setPosition(dimLayer, 10, 10);
+ }
+
+ private SurfaceControl getDimLayer() {
+ return mDimmer.mDimState.mDimLayer;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 2284bbb..63ac4af 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -28,6 +28,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -38,7 +39,6 @@
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.graphics.Path;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -166,6 +166,7 @@
assertTrue(appWin.canBeImeTarget());
WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
assertEquals(appWin, imeTarget);
+ appWin.mHidden = false;
// Verify that an child window can be an ime target.
final WindowState childWin = createWindow(appWin,
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index f860195..26a7313 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -19,12 +19,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.graphics.Point;
import android.graphics.Rect;
-import android.platform.test.annotations.Postsubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -137,6 +137,32 @@
}
@Test
+ public void testTimeout_scaled() throws Exception {
+ sWm.setAnimationScale(2, 5.0f);
+ try{
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner, never()).onAnimationCancelled();
+
+ mClock.fastForward(10000);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner).onAnimationCancelled();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ } finally {
+ sWm.setAnimationScale(2, 1.0f);
+ }
+
+ }
+
+ @Test
public void testZeroAnimations() throws Exception {
mController.goodToGo();
verifyZeroInteractions(mMockRunner);
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..51b019a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,50 @@
+package com.android.server.wm;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link RootWindowContainer} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RootWindowContainerTests extends WindowTestsBase {
+ @Test
+ public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
+ // Add first stack we expect to be updated with configuration change.
+ final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+ stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
+
+ // Add second task that will be set for deferred removal that should not be returned
+ // with the configuration change.
+ final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
+ deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
+ new Rect(0, 0, 5, 5));
+ deferredDeletedStack.mDeferRemoval = true;
+
+ final Configuration override = new Configuration(
+ mDisplayContent.getOverrideConfiguration());
+ override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+
+ // Set display override.
+ final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+ mDisplayContent.getDisplayId());
+
+ // Ensure only first stack is returned.
+ assertTrue(results.length == 1);
+ assertTrue(results[0] == stack.mStackId);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index f23bd62..3a1485e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.graphics.Color.BLUE;
import static android.graphics.Color.RED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
@@ -27,7 +26,6 @@
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -49,6 +47,7 @@
import android.os.Handler;
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.util.Pair;
@@ -66,18 +65,19 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.function.BooleanSupplier;
/**
* Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
+ * atest FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
*/
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
// TODO: Test non-Activity windows.
@SmallTest
-// TODO(b/68957554)
-//@Presubmit
+@Presubmit
+@FlakyTest(bugId = 68957554)
@RunWith(AndroidJUnit4.class)
public class ScreenDecorWindowTests {
@@ -123,40 +123,33 @@
public void testScreenSides() throws Exception {
// Decor on top
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
// Decor at the bottom
updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness);
// Decor to the left
updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness);
// Decor to the right
updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetRight(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
}
@Test
public void testMultipleDecors() throws Exception {
// Test 2 decor windows on-top.
createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mHalfDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness);
createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
// And one at the bottom.
createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness);
- insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
- assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), mHalfDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness);
}
@Test
@@ -164,18 +157,15 @@
WindowInsets initialInsets = getInsets(mTestActivity);
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertEquals(mDecorThickness, insets.getSystemWindowInsetTop());
+ assertTopInsetEquals(mTestActivity, mDecorThickness);
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
0, PRIVATE_FLAG_IS_SCREEN_DECOR);
- insets = getInsets(mTestActivity);
- assertEquals(initialInsets.getSystemWindowInsetTop(), insets.getSystemWindowInsetTop());
+ assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
- insets = getInsets(mTestActivity);
- assertEquals(mDecorThickness, insets.getSystemWindowInsetTop());
+ assertTopInsetEquals(mTestActivity, mDecorThickness);
}
@Test
@@ -183,17 +173,10 @@
WindowInsets initialInsets = getInsets(mTestActivity);
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
- WindowInsets insets = getInsets(mTestActivity);
- assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
removeWindow(decorWindow);
- insets = getInsets(mTestActivity);
- assertEquals(initialInsets.getSystemWindowInsetTop(), insets.getSystemWindowInsetTop());
- }
-
- private View createAppWindow() {
- return createWindow("appWindow", TOP, MATCH_PARENT, MATCH_PARENT, BLUE,
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR, 0);
+ assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
}
private View createDecorWindow(int gravity, int width, int height) {
@@ -249,10 +232,6 @@
waitForIdle();
}
- private WindowInsets getInsets(View v) {
- return new WindowInsets(v.getRootWindowInsets());
- }
-
private WindowInsets getInsets(Activity a) {
return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets());
}
@@ -269,11 +248,59 @@
lp.flags = (lp.flags & ~mask) | (flags & mask);
}
+ /**
+ * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
+ */
+ private void assertTopInsetEquals(Activity activity, int expected) throws Exception {
+ waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+ assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
+ }
+
+ /**
+ * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
+ * waiting as needed.
+ */
+ private void assertInsetGreaterOrEqual(Activity activity, int side, int expected)
+ throws Exception {
+ waitFor(() -> {
+ final WindowInsets insets = getInsets(activity);
+ switch (side) {
+ case TOP: return insets.getSystemWindowInsetTop() >= expected;
+ case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected;
+ case LEFT: return insets.getSystemWindowInsetLeft() >= expected;
+ case RIGHT: return insets.getSystemWindowInsetRight() >= expected;
+ default: return true;
+ }
+ });
+
+ final WindowInsets insets = getInsets(activity);
+ switch (side) {
+ case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
+ case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
+ case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
+ case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
+ }
+ }
+
/** Asserts that the first entry is greater than or equal to the second entry. */
private void assertGreaterOrEqual(int first, int second) throws Exception {
Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
}
+ private void waitFor(BooleanSupplier waitCondition) {
+ int retriesLeft = 5;
+ do {
+ if (waitCondition.getAsBoolean()) {
+ break;
+ }
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // Well I guess we are not waiting...
+ }
+ } while (retriesLeft-- > 0);
+ }
+
private void finishActivity(Activity a) {
if (a == null) {
return;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 81fd889..000cf38 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -69,11 +69,6 @@
private Runnable mRunnableWhenAddingSplashScreen;
static synchronized WindowManagerService getWindowManagerService(Context context) {
- return getWindowManagerService(context, new SurfaceAnimationRunner());
- }
-
- static synchronized WindowManagerService getWindowManagerService(Context context,
- SurfaceAnimationRunner surfaceAnimationRunner) {
if (sWm == null) {
// We only want to do this once for the test process as we don't want WM to try to
// register a bunch of local services again.
@@ -111,7 +106,7 @@
}
sWm = WindowManagerService.main(context, ims, true, false,
- false, new TestWindowManagerPolicy(), surfaceAnimationRunner);
+ false, new TestWindowManagerPolicy());
}
return sWm;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 6a4710b..4d41718 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -36,6 +36,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -57,15 +58,17 @@
final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
- assertFalse(parentWindow.mHidden);
+ // parentWindow is initially set to hidden.
+ assertTrue(parentWindow.mHidden);
+ assertFalse(parentWindow.isParentWindowHidden());
+ assertTrue(child1.isParentWindowHidden());
+ assertTrue(child2.isParentWindowHidden());
+
+ parentWindow.mHidden = false;
assertFalse(parentWindow.isParentWindowHidden());
assertFalse(child1.isParentWindowHidden());
assertFalse(child2.isParentWindowHidden());
- parentWindow.mHidden = true;
- assertFalse(parentWindow.isParentWindowHidden());
- assertTrue(child1.isParentWindowHidden());
- assertTrue(child2.isParentWindowHidden());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 7918901..69b1378 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -84,16 +84,6 @@
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
- private final SurfaceAnimationRunner mSurfaceAnimationRunner;
-
- public WindowTestsBase() {
- this(new SurfaceAnimationRunner());
- }
-
- public WindowTestsBase(SurfaceAnimationRunner surfaceAnimationRunner) {
- mSurfaceAnimationRunner = surfaceAnimationRunner;
- }
-
@Before
public void setUp() throws Exception {
if (!sOneTimeSetupDone) {
@@ -108,7 +98,7 @@
final Context context = InstrumentationRegistry.getTargetContext();
AttributeCache.init(context);
- sWm = TestWindowManagerPolicy.getWindowManagerService(context, mSurfaceAnimationRunner);
+ sWm = TestWindowManagerPolicy.getWindowManagerService(context);
beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 99eb846..e36586e 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -210,6 +210,9 @@
runCommand(instrumentation, "cmd package set-home-activity --user "
+ instrumentation.getContext().getUserId() + " " + component,
result -> result.contains("Success"));
+ runCommand(instrumentation, "cmd shortcut clear-default-launcher --user "
+ + instrumentation.getContext().getUserId(),
+ result -> result.contains("Success"));
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9ae6f00..7b2c040 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -49,8 +50,10 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
+import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
@@ -64,9 +67,11 @@
import android.content.pm.ParceledListSlice;
import android.graphics.Color;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings.Secure;
@@ -117,7 +122,7 @@
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int mUid = Binder.getCallingUid();
- private NotificationManagerService mService;
+ private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
@Mock
@@ -149,20 +154,28 @@
@Mock private ICompanionDeviceManager mCompanionMgr;
@Mock SnoozeHelper mSnoozeHelper;
@Mock GroupHelper mGroupHelper;
+ @Mock
+ IBinder mPermOwner;
+ @Mock
+ IActivityManager mAm;
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
+ int countSystemChecks = 0;
+
public TestableNotificationManagerService(Context context) {
super(context);
}
@Override
protected boolean isCallingUidSystem() {
+ countSystemChecks++;
return true;
}
@Override
protected boolean isCallerSystemOrPhone() {
+ countSystemChecks++;
return true;
}
@@ -175,6 +188,11 @@
protected void reportSeen(NotificationRecord r) {
return;
}
+
+ @Override
+ protected void reportUserInteraction(NotificationRecord r) {
+ return;
+ }
}
@Before
@@ -204,6 +222,7 @@
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
+ when(mAm.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -234,7 +253,7 @@
mPackageManager, mPackageManagerClient, mockLightsManager,
mListeners, mAssistants, mConditionProviders,
mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
- mGroupHelper);
+ mGroupHelper, mAm);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
@@ -588,6 +607,8 @@
mBinderService.getActiveNotifications(PKG);
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(
+ any(), any(), anyInt(), anyInt());
}
@Test
@@ -606,6 +627,7 @@
ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -620,6 +642,7 @@
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -633,6 +656,7 @@
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
assertEquals(0, mService.getNotificationRecordCount());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -654,6 +678,7 @@
ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -672,6 +697,7 @@
mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
waitForIdle();
assertEquals(0, mService.getNotificationRecordCount());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -685,6 +711,7 @@
waitForIdle();
assertEquals(0, mService.getNotificationRecordCount());
+ verify(mAm, atLeastOnce()).revokeUriPermissionFromOwner(any(), any(), anyInt(), anyInt());
}
@Test
@@ -1225,7 +1252,7 @@
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
assertEquals(mTestNotificationChannel.getId(), captor.getValue().getStringExtra(
- NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+ NotificationManager.EXTRA_NOTIFICATION_CHANNEL_ID));
assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
}
@@ -1247,7 +1274,7 @@
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
assertEquals(mTestNotificationChannel.getId(), captor.getValue().getStringExtra(
- NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+ NotificationManager.EXTRA_NOTIFICATION_CHANNEL_ID));
assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
}
@@ -1283,7 +1310,7 @@
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
assertEquals(existing.getId(), captor.getValue().getStringExtra(
- NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+ NotificationManager.EXTRA_NOTIFICATION_CHANNEL_GROUP_ID));
assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
}
@@ -1304,7 +1331,7 @@
captor.getValue().getAction());
assertEquals(PKG, captor.getValue().getPackage());
assertEquals(existing.getId(), captor.getValue().getStringExtra(
- NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+ NotificationManager.EXTRA_NOTIFICATION_CHANNEL_GROUP_ID));
assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
}
@@ -2429,4 +2456,78 @@
.setUid(user2.sbn.getUid())
.setLastNotified(user2.sbn.getPostTime())));
}
+
+ @Test
+ public void testRestore() throws Exception {
+ int systemChecks = mService.countSystemChecks;
+ mBinderService.applyRestore(null, UserHandle.USER_SYSTEM);
+ assertEquals(1, mService.countSystemChecks - systemChecks);
+ }
+
+ @Test
+ public void testBackup() throws Exception {
+ int systemChecks = mService.countSystemChecks;
+ mBinderService.getBackupPayload(1);
+ assertEquals(1, mService.countSystemChecks - systemChecks);
+ }
+
+ @Test
+ public void revokeUriPermissions_update() throws Exception {
+ NotificationChannel c = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ Message message1 = new Message("", 0, "");
+ message1.setData("", Uri.fromParts("old", "", "old stuff"));
+ Message message2 = new Message("", 1, "");
+ message2.setData("", Uri.fromParts("new", "", "new stuff"));
+
+ Notification.Builder nb = new Notification.Builder(mContext, c.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(message1)
+ .addMessage(message2));
+ StatusBarNotification oldSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord oldRecord =
+ new NotificationRecord(mContext, oldSbn, c);
+
+ Notification.Builder nb1 = new Notification.Builder(mContext, c.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.MessagingStyle("").addMessage(message2));
+ StatusBarNotification newSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord newRecord =
+ new NotificationRecord(mContext, newSbn, c);
+
+ mService.revokeUriPermissions(newRecord, oldRecord);
+
+ verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()),
+ anyInt(), anyInt());
+ }
+
+ @Test
+ public void revokeUriPermissions_cancel() throws Exception {
+ NotificationChannel c = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ Message message1 = new Message("", 0, "");
+ message1.setData("", Uri.fromParts("old", "", "old stuff"));
+
+ Notification.Builder nb = new Notification.Builder(mContext, c.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(message1));
+ StatusBarNotification oldSbn = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord oldRecord =
+ new NotificationRecord(mContext, oldSbn, c);
+
+ mService.revokeUriPermissions(null, oldRecord);
+
+ verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()),
+ anyInt(), anyInt());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
index 7c14d08..bc28150 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.util.Xml.Encoding;
@@ -67,6 +68,15 @@
}
@Test
+ public void testRemoveAccess() {
+ mAccessList.grantFullAccess("pkg", 0);
+ assertTrue(mAccessList.hasFullAccess("pkg", 0));
+
+ mAccessList.removeGrant("pkg", 0);
+ assertFalse(mAccessList.hasFullAccess("pkg", 0));
+ }
+
+ @Test
public void testSerialization() throws XmlPullParserException, IOException {
mAccessList.grantFullAccess("pkg", 0);
mAccessList.grantFullAccess("pkg1", 0);
@@ -76,7 +86,7 @@
ByteArrayOutputStream output = new ByteArrayOutputStream();
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(output, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ mAccessList.writeXml(out, UserHandle.USER_ALL);
out.flush();
assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name()));
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 2becdf2..b654a66 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -228,7 +228,9 @@
}
if (timeout > elapsedRealtime) {
// Convert to elapsed timebase
- appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
+ appUsageHistory.bucketTimeoutTime =
+ Math.max(appUsageHistory.bucketTimeoutTime,
+ mElapsedDuration + (timeout - mElapsedSnapshot));
}
}
appUsageHistory.bucketingReason = REASON_USAGE;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 8cb2eec..32db752 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -177,12 +177,20 @@
long mAppIdleParoleDurationMillis;
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
+ /** Minimum time a strong usage event should keep the bucket elevated. */
+ long mStrongUsageTimeoutMillis;
+ /** Minimum time a notification seen event should keep the bucket elevated. */
+ long mNotificationSeenTimeoutMillis;
+ /** Minimum time a system update event should keep the buckets elevated. */
+ long mSystemUpdateUsageTimeoutMillis;
volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
boolean mCharging;
private long mLastAppIdleParoledTime;
private boolean mSystemServicesReady = false;
+ // There was a system update, defaults need to be initialized after services are ready
+ private boolean mPendingInitializeDefaults;
private final DeviceStateReceiver mDeviceStateReceiver;
@@ -279,6 +287,7 @@
public void onBootPhase(int phase) {
mInjector.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ Slog.d(TAG, "Setting app idle enabled state");
setAppIdleEnabled(mInjector.isAppIdleEnabled());
// Observe changes to the threshold
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
@@ -293,11 +302,15 @@
mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
}
+ mSystemServicesReady = true;
+
+ if (mPendingInitializeDefaults) {
+ initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
+ }
+
if (mPendingOneTimeCheckIdleStates) {
postOneTimeCheckIdleStates();
}
-
- mSystemServicesReady = true;
} else if (phase == PHASE_BOOT_COMPLETED) {
setChargingState(mInjector.isCharging());
}
@@ -323,7 +336,7 @@
synchronized (mAppIdleLock) {
AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
STANDBY_BUCKET_ACTIVE, elapsedRealtime,
- elapsedRealtime + 2 * ONE_HOUR);
+ elapsedRealtime + mStrongUsageTimeoutMillis);
maybeInformListeners(packageName, userId, elapsedRealtime,
appUsage.currentBucket, false);
}
@@ -451,7 +464,8 @@
UserHandle.getAppId(pi.applicationInfo.uid),
userId);
if (DEBUG) {
- Slog.d(TAG, " Checking idle state for " + packageName);
+ Slog.d(TAG, " Checking idle state for " + packageName + " special=" +
+ isSpecial);
}
if (isSpecial) {
synchronized (mAppIdleLock) {
@@ -523,6 +537,7 @@
elapsedRealtime, bucket)) {
StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
bucket, userStartedInteracting);
+ if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
StandbyUpdateRecord.obtain(packageName, userId,
bucket, userStartedInteracting)));
@@ -530,6 +545,7 @@
}
}
+ @GuardedBy("mAppIdleLock")
@StandbyBuckets int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
@@ -618,11 +634,11 @@
if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
STANDBY_BUCKET_WORKING_SET,
- elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR);
+ elapsedRealtime, elapsedRealtime + mNotificationSeenTimeoutMillis);
} else {
mAppIdleHistory.reportUsage(event.mPackage, userId,
STANDBY_BUCKET_ACTIVE,
- elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR);
+ elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
}
final boolean userStartedInteracting =
@@ -1087,7 +1103,13 @@
}
void initializeDefaultsForSystemApps(int userId) {
- Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+ if (!mSystemServicesReady) {
+ // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
+ mPendingInitializeDefaults = true;
+ return;
+ }
+ Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
+ + "appIdleEnabled=" + mAppIdleEnabled);
final long elapsedRealtime = mInjector.elapsedRealtime();
List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
PackageManager.MATCH_DISABLED_COMPONENTS,
@@ -1098,15 +1120,10 @@
final PackageInfo pi = packages.get(i);
String packageName = pi.packageName;
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- // Mark app as used for 4 hours. After that it can timeout to whatever the
+ // Mark app as used for 2 hours. After that it can timeout to whatever the
// past usage pattern was.
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
- elapsedRealtime + 4 * ONE_HOUR);
- if (isAppSpecial(packageName, UserHandle.getAppId(pi.applicationInfo.uid),
- userId)) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
- }
+ elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
}
}
}
@@ -1385,6 +1402,12 @@
private static final String KEY_PAROLE_DURATION = "parole_duration";
private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
+ private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
+ private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
+ "notification_seen_duration";
+ private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
+ "system_update_usage_duration";
+
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1445,7 +1468,15 @@
ELAPSED_TIME_THRESHOLDS);
mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
-
+ mStrongUsageTimeoutMillis = mParser.getDurationMillis
+ (KEY_STRONG_USAGE_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR);
+ mNotificationSeenTimeoutMillis = mParser.getDurationMillis
+ (KEY_NOTIFICATION_SEEN_HOLD_DURATION,
+ COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR);
+ mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
+ (KEY_SYSTEM_UPDATE_HOLD_DURATION,
+ COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 36a2a95..3b0fd1f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -654,7 +654,7 @@
public boolean isAppInactive(String packageName, int userId) {
try {
userId = ActivityManager.getService().handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, true, "isAppInactive", null);
+ Binder.getCallingUid(), userId, false, false, "isAppInactive", null);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
new file mode 100644
index 0000000..7480e56
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 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.usb;
+
+import android.annotation.NonNull;
+import android.service.usb.UsbAlsaDeviceProto;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+/**
+ * Represents the ALSA specification, and attributes of an ALSA device.
+ */
+public final class UsbAlsaDevice {
+ private static final String TAG = "UsbAlsaDevice";
+ protected static final boolean DEBUG = false;
+
+ private final int mCardNum;
+ private final int mDeviceNum;
+ private final boolean mHasPlayback;
+ private final boolean mHasCapture;
+
+ private final boolean mIsInputHeadset;
+ private final boolean mIsOutputHeadset;
+
+ private final String mDeviceAddress;
+
+ private String mDeviceName = "";
+ private String mDeviceDescription = "";
+
+ public UsbAlsaDevice(int card, int device, String deviceAddress,
+ boolean hasPlayback, boolean hasCapture,
+ boolean isInputHeadset, boolean isOutputHeadset) {
+ mCardNum = card;
+ mDeviceNum = device;
+ mDeviceAddress = deviceAddress;
+ mHasPlayback = hasPlayback;
+ mHasCapture = hasCapture;
+ mIsInputHeadset = isInputHeadset;
+ mIsOutputHeadset = isOutputHeadset;
+ }
+
+ /**
+ * @returns the ALSA card number associated with this peripheral.
+ */
+ public int getCardNum() {
+ return mCardNum;
+ }
+
+ /**
+ * @returns the ALSA device number associated with this peripheral.
+ */
+ public int getDeviceNum() {
+ return mDeviceNum;
+ }
+
+ /**
+ * @returns the USB device device address associated with this peripheral.
+ */
+ public String getDeviceAddress() {
+ return mDeviceAddress;
+ }
+
+ /**
+ * @returns true if the device supports playback.
+ */
+ public boolean hasPlayback() {
+ return mHasPlayback;
+ }
+
+ /**
+ * @returns true if the device supports capture (recording).
+ */
+ public boolean hasCapture() {
+ return mHasCapture;
+ }
+
+ /**
+ * @returns true if the device is a headset for purposes of capture.
+ */
+ public boolean isInputHeadset() {
+ return mIsInputHeadset;
+ }
+
+ /**
+ * @returns true if the device is a headset for purposes of playback.
+ */
+ public boolean isOutputHeadset() {
+ return mIsOutputHeadset;
+ }
+
+ /**
+ * @Override
+ * @returns a string representation of the object.
+ */
+ public String toString() {
+ return "UsbAlsaDevice: [card: " + mCardNum
+ + ", device: " + mDeviceNum
+ + ", name: " + mDeviceName
+ + ", hasPlayback: " + mHasPlayback
+ + ", hasCapture: " + mHasCapture + "]";
+ }
+
+ /**
+ * Write a description of the device to a dump stream.
+ */
+ public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum);
+ dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum);
+ dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName);
+ dump.write("has_playback", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasPlayback);
+ dump.write("has_capture", UsbAlsaDeviceProto.HAS_CAPTURE, mHasCapture);
+ dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress);
+
+ dump.end(token);
+ }
+
+ // called by logDevices
+ String toShortString() {
+ return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]";
+ }
+
+ String getDeviceName() {
+ return mDeviceName;
+ }
+
+ void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
+ mDeviceName = deviceName;
+ mDeviceDescription = deviceDescription;
+ }
+
+ /**
+ * @Override
+ * @returns true if the objects are equivalent.
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof UsbAlsaDevice)) {
+ return false;
+ }
+ UsbAlsaDevice other = (UsbAlsaDevice) obj;
+ return (mCardNum == other.mCardNum
+ && mDeviceNum == other.mDeviceNum
+ && mHasPlayback == other.mHasPlayback
+ && mHasCapture == other.mHasCapture
+ && mIsInputHeadset == other.mIsInputHeadset
+ && mIsOutputHeadset == other.mIsOutputHeadset);
+ }
+
+ /**
+ * @Override
+ * @returns a hash code generated from the object contents.
+ */
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mCardNum;
+ result = prime * result + mDeviceNum;
+ result = prime * result + (mHasPlayback ? 0 : 1);
+ result = prime * result + (mHasCapture ? 0 : 1);
+ result = prime * result + (mIsInputHeadset ? 0 : 1);
+ result = prime * result + (mIsOutputHeadset ? 0 : 1);
+
+ return result;
+ }
+}
+
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 7bea8a1..0c5f8f1 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -10,7 +10,7 @@
* 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 an
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
@@ -19,28 +19,25 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.midi.MidiDeviceInfo;
import android.os.Bundle;
-import android.os.FileObserver;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.provider.Settings;
+import android.service.usb.UsbAlsaManagerProto;
import android.util.Slog;
import com.android.internal.alsa.AlsaCardsParser;
-import com.android.internal.alsa.AlsaDevicesParser;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.audio.AudioService;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
import libcore.io.IoUtils;
-import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -57,107 +54,36 @@
private final boolean mHasMidiFeature;
private final AlsaCardsParser mCardsParser = new AlsaCardsParser();
- private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser();
// this is needed to map USB devices to ALSA Audio Devices, especially to remove an
// ALSA device when we are notified that its associated USB device has been removed.
+ private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>();
- private final HashMap<UsbDevice,UsbAudioDevice>
- mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>();
-
- private boolean mIsInputHeadset; // as reported by UsbDescriptorParser
- private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser
-
- private final HashMap<UsbDevice,UsbMidiDevice>
- mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
-
- private final HashMap<String,AlsaDevice>
- mAlsaDevices = new HashMap<String,AlsaDevice>();
-
- private UsbAudioDevice mAccessoryAudioDevice = null;
+ /**
+ * List of connected MIDI devices
+ */
+ private final HashMap<String, UsbMidiDevice>
+ mMidiDevices = new HashMap<String, UsbMidiDevice>();
// UsbMidiDevice for USB peripheral mode (gadget) device
private UsbMidiDevice mPeripheralMidiDevice = null;
- private final class AlsaDevice {
- public static final int TYPE_UNKNOWN = 0;
- public static final int TYPE_PLAYBACK = 1;
- public static final int TYPE_CAPTURE = 2;
- public static final int TYPE_MIDI = 3;
-
- public int mCard;
- public int mDevice;
- public int mType;
-
- public AlsaDevice(int type, int card, int device) {
- mType = type;
- mCard = card;
- mDevice = device;
- }
-
- public boolean equals(Object obj) {
- if (! (obj instanceof AlsaDevice)) {
- return false;
- }
- AlsaDevice other = (AlsaDevice)obj;
- return (mType == other.mType && mCard == other.mCard && mDevice == other.mDevice);
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("AlsaDevice: [card: " + mCard);
- sb.append(", device: " + mDevice);
- sb.append(", type: " + mType);
- sb.append("]");
- return sb.toString();
- }
- }
-
- private final FileObserver mAlsaObserver = new FileObserver(ALSA_DIRECTORY,
- FileObserver.CREATE | FileObserver.DELETE) {
- public void onEvent(int event, String path) {
- switch (event) {
- case FileObserver.CREATE:
- alsaFileAdded(path);
- break;
- case FileObserver.DELETE:
- alsaFileRemoved(path);
- break;
- }
- }
- };
-
/* package */ UsbAlsaManager(Context context) {
mContext = context;
mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
-
- // initial scan
- if (mCardsParser.scan() != AlsaCardsParser.SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error scanning ASLA cards file.");
- }
}
public void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
-
- mAlsaObserver.startWatching();
-
- // add existing alsa devices
- File[] files = new File(ALSA_DIRECTORY).listFiles();
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- alsaFileAdded(files[i].getName());
- }
- }
}
// Notifies AudioService when a device is added or removed
// audioDevice - the AudioDevice that was added or removed
// enabled - if true, we're connecting a device (it's arrived), else disconnecting
- private void notifyDeviceState(UsbAudioDevice audioDevice, boolean enabled) {
+ private void notifyDeviceState(UsbAlsaDevice alsaDevice, boolean enabled) {
if (DEBUG) {
- Slog.d(TAG, "notifyDeviceState " + enabled + " " + audioDevice);
+ Slog.d(TAG, "notifyDeviceState " + enabled + " " + alsaDevice);
}
if (mAudioService == null) {
@@ -177,276 +103,151 @@
}
int state = (enabled ? 1 : 0);
- int alsaCard = audioDevice.mCard;
- int alsaDevice = audioDevice.mDevice;
- if (alsaCard < 0 || alsaDevice < 0) {
- Slog.e(TAG, "Invalid alsa card or device alsaCard: " + alsaCard +
- " alsaDevice: " + alsaDevice);
+ int cardNum = alsaDevice.getCardNum();
+ int deviceNum = alsaDevice.getDeviceNum();
+ if (cardNum < 0 || deviceNum < 0) {
+ Slog.e(TAG, "Invalid alsa card or device alsaCard: " + cardNum
+ + " alsaDevice: " + deviceNum);
return;
}
- String address = AudioService.makeAlsaAddressString(alsaCard, alsaDevice);
+ String address = AudioService.makeAlsaAddressString(cardNum, deviceNum);
try {
// Playback Device
- if (audioDevice.mHasPlayback) {
- int device;
- if (mIsOutputHeadset) {
- device = AudioSystem.DEVICE_OUT_USB_HEADSET;
- } else {
- device = (audioDevice == mAccessoryAudioDevice
- ? AudioSystem.DEVICE_OUT_USB_ACCESSORY
- : AudioSystem.DEVICE_OUT_USB_DEVICE);
- }
+ if (alsaDevice.hasPlayback()) {
+ int device = alsaDevice.isOutputHeadset()
+ ? AudioSystem.DEVICE_OUT_USB_HEADSET
+ : AudioSystem.DEVICE_OUT_USB_DEVICE;
if (DEBUG) {
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
- " addr:" + address + " name:" + audioDevice.getDeviceName());
+ " addr:" + address + " name:" + alsaDevice.getDeviceName());
}
mAudioService.setWiredDeviceConnectionState(
- device, state, address, audioDevice.getDeviceName(), TAG);
+ device, state, address, alsaDevice.getDeviceName(), TAG);
}
// Capture Device
- if (audioDevice.mHasCapture) {
- int device;
- if (mIsInputHeadset) {
- device = AudioSystem.DEVICE_IN_USB_HEADSET;
- } else {
- device = (audioDevice == mAccessoryAudioDevice
- ? AudioSystem.DEVICE_IN_USB_ACCESSORY
- : AudioSystem.DEVICE_IN_USB_DEVICE);
- }
+ if (alsaDevice.hasCapture()) {
+ int device = alsaDevice.isInputHeadset()
+ ? AudioSystem.DEVICE_IN_USB_HEADSET
+ : AudioSystem.DEVICE_IN_USB_DEVICE;
mAudioService.setWiredDeviceConnectionState(
- device, state, address, audioDevice.getDeviceName(), TAG);
+ device, state, address, alsaDevice.getDeviceName(), TAG);
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
}
}
- private AlsaDevice waitForAlsaDevice(int card, int device, int type) {
- if (DEBUG) {
- Slog.e(TAG, "waitForAlsaDevice(c:" + card + " d:" + device + ")");
+ private int getAlsaDeviceListIndexFor(String deviceAddress) {
+ for (int index = 0; index < mAlsaDevices.size(); index++) {
+ if (mAlsaDevices.get(index).getDeviceAddress().equals(deviceAddress)) {
+ return index;
+ }
}
-
- AlsaDevice testDevice = new AlsaDevice(type, card, device);
-
- // This value was empirically determined.
- final int kWaitTimeMs = 2500;
-
- synchronized(mAlsaDevices) {
- long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs;
- do {
- if (mAlsaDevices.values().contains(testDevice)) {
- return testDevice;
- }
- long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime();
- if (waitTimeMs > 0) {
- try {
- mAlsaDevices.wait(waitTimeMs);
- } catch (InterruptedException e) {
- Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
- }
- }
- } while (timeoutMs > SystemClock.elapsedRealtime());
- }
-
- Slog.e(TAG, "waitForAlsaDevice failed for " + testDevice);
- return null;
+ return -1;
}
- private void alsaFileAdded(String name) {
- Slog.i(TAG, "alsaFileAdded(" + name + ")");
- int type = AlsaDevice.TYPE_UNKNOWN;
- int card = -1, device = -1;
-
- if (name.startsWith("pcmC")) {
- if (name.endsWith("p")) {
- type = AlsaDevice.TYPE_PLAYBACK;
- } else if (name.endsWith("c")) {
- type = AlsaDevice.TYPE_CAPTURE;
- }
- } else if (name.startsWith("midiC")) {
- type = AlsaDevice.TYPE_MIDI;
- }
-
- if (type != AlsaDevice.TYPE_UNKNOWN) {
- try {
- int c_index = name.indexOf('C');
- int d_index = name.indexOf('D');
- int end = name.length();
- if (type == AlsaDevice.TYPE_PLAYBACK || type == AlsaDevice.TYPE_CAPTURE) {
- // skip trailing 'p' or 'c'
- end--;
- }
- card = Integer.parseInt(name.substring(c_index + 1, d_index));
- device = Integer.parseInt(name.substring(d_index + 1, end));
- } catch (Exception e) {
- Slog.e(TAG, "Could not parse ALSA file name " + name, e);
- return;
- }
- synchronized(mAlsaDevices) {
- if (mAlsaDevices.get(name) == null) {
- AlsaDevice alsaDevice = new AlsaDevice(type, card, device);
- Slog.d(TAG, "Adding ALSA device " + alsaDevice);
- mAlsaDevices.put(name, alsaDevice);
- mAlsaDevices.notifyAll();
- }
- }
+ private UsbAlsaDevice removeAlsaDeviceFromList(String deviceAddress) {
+ int index = getAlsaDeviceListIndexFor(deviceAddress);
+ if (index > -1) {
+ return mAlsaDevices.remove(index);
+ } else {
+ return null;
}
}
- private void alsaFileRemoved(String path) {
- synchronized(mAlsaDevices) {
- AlsaDevice device = mAlsaDevices.remove(path);
- if (device != null) {
- Slog.d(TAG, "ALSA device removed: " + device);
- }
- }
- }
-
- /*
- * Select the default device of the specified card.
- */
- /* package */ UsbAudioDevice selectAudioCard(int card) {
- if (DEBUG) {
- Slog.d(TAG, "selectAudioCard() card:" + card
- + " isCardUsb(): " + mCardsParser.isCardUsb(card));
- }
- if (!mCardsParser.isCardUsb(card)) {
- // Don't. AudioPolicyManager has logic for falling back to internal devices.
- return null;
- }
-
- if (mDevicesParser.scan() != AlsaDevicesParser.SCANSTATUS_SUCCESS) {
- Slog.e(TAG, "Error parsing ALSA devices file.");
- return null;
- }
-
- int device = mDevicesParser.getDefaultDeviceNum(card);
-
- boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
- boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
- if (DEBUG) {
- Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
- }
-
- int deviceClass =
- (mCardsParser.isCardUsb(card)
- ? UsbAudioDevice.kAudioDeviceClass_External
- : UsbAudioDevice.kAudioDeviceClass_Internal) |
- UsbAudioDevice.kAudioDeviceMeta_Alsa;
-
- // Playback device file needed/present?
- if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) {
- return null;
- }
-
- // Capture device file needed/present?
- if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) {
- return null;
- }
-
- UsbAudioDevice audioDevice =
- new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass);
- AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
- audioDevice.setDeviceNameAndDescription(cardRecord.mCardName, cardRecord.mCardDescription);
-
- notifyDeviceState(audioDevice, true /*enabled*/);
-
- return audioDevice;
- }
-
- /* package */ UsbAudioDevice selectDefaultDevice() {
+ /* package */ UsbAlsaDevice selectDefaultDevice() {
if (DEBUG) {
Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
}
- return selectAudioCard(mCardsParser.getDefaultCard());
+
+ if (mAlsaDevices.size() > 0) {
+ UsbAlsaDevice alsaDevice = mAlsaDevices.get(0);
+ if (DEBUG) {
+ Slog.d(TAG, " alsaDevice:" + alsaDevice);
+ }
+ if (alsaDevice != null) {
+ notifyDeviceState(alsaDevice, true /*enabled*/);
+ }
+ return alsaDevice;
+ } else {
+ return null;
+ }
}
- /* package */ void usbDeviceAdded(UsbDevice usbDevice,
- boolean isInputHeadset, boolean isOutputHeadset) {
+ /* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
+ UsbDescriptorParser parser) {
if (DEBUG) {
- Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
+ Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName()
+ " nm:" + usbDevice.getProductName());
}
- mIsInputHeadset = isInputHeadset;
- mIsOutputHeadset = isOutputHeadset;
+ // Scan the Alsa File Space
+ mCardsParser.scan();
- // Is there an audio interface in there?
- boolean isAudioDevice = false;
-
- // FIXME - handle multiple configurations?
- int interfaceCount = usbDevice.getInterfaceCount();
- for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < interfaceCount;
- ntrfaceIndex++) {
- UsbInterface ntrface = usbDevice.getInterface(ntrfaceIndex);
- if (ntrface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
- isAudioDevice = true;
- }
- }
-
- if (DEBUG) {
- Slog.d(TAG, " isAudioDevice: " + isAudioDevice);
- }
- if (!isAudioDevice) {
+ // Find the ALSA spec for this device address
+ AlsaCardsParser.AlsaCardRecord cardRec =
+ mCardsParser.findCardNumFor(deviceAddress);
+ if (cardRec == null) {
return;
}
- int addedCard = mCardsParser.getDefaultUsbCard();
-
- // If the default isn't a USB device, let the existing "select internal mechanism"
- // handle the selection.
+ // Add it to the devices list
+ boolean hasInput = parser.hasInput();
+ boolean hasOutput = parser.hasOutput();
if (DEBUG) {
- Slog.d(TAG, " mCardsParser.isCardUsb(" + addedCard + ") = "
- + mCardsParser.isCardUsb(addedCard));
+ Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput);
}
- if (mCardsParser.isCardUsb(addedCard)) {
- UsbAudioDevice audioDevice = selectAudioCard(addedCard);
- if (audioDevice != null) {
- mAudioDevices.put(usbDevice, audioDevice);
- Slog.i(TAG, "USB Audio Device Added: " + audioDevice);
+ if (hasInput || hasOutput) {
+ boolean isInputHeadset = parser.isInputHeadset();
+ boolean isOutputHeadset = parser.isOutputHeadset();
+ UsbAlsaDevice alsaDevice =
+ new UsbAlsaDevice(cardRec.getCardNum(), 0 /*device*/, deviceAddress,
+ hasOutput, hasInput, isInputHeadset, isOutputHeadset);
+ alsaDevice.setDeviceNameAndDescription(
+ cardRec.getCardName(), cardRec.getCardDescription());
+ mAlsaDevices.add(0, alsaDevice);
+
+ // Select it
+ if (alsaDevice != null) {
+ notifyDeviceState(alsaDevice, true /*enabled*/);
}
+ }
- // look for MIDI devices
+ // look for MIDI devices
+ boolean hasMidi = parser.hasMIDIInterface();
+ if (DEBUG) {
+ Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
+ }
+ if (hasMidi && mHasMidiFeature) {
+ int device = 0;
+ Bundle properties = new Bundle();
+ String manufacturer = usbDevice.getManufacturerName();
+ String product = usbDevice.getProductName();
+ String version = usbDevice.getVersion();
+ String name;
+ if (manufacturer == null || manufacturer.isEmpty()) {
+ name = product;
+ } else if (product == null || product.isEmpty()) {
+ name = manufacturer;
+ } else {
+ name = manufacturer + " " + product;
+ }
+ properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
+ properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
+ properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
+ properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
+ properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+ usbDevice.getSerialNumber());
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
+ properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
- // Don't need to call mDevicesParser.scan() because selectAudioCard() does this above.
- // Uncomment this next line if that behavior changes in the fugure.
- // mDevicesParser.scan()
-
- boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard);
- if (hasMidi && mHasMidiFeature) {
- int device = mDevicesParser.getDefaultDeviceNum(addedCard);
- AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
- if (alsaDevice != null) {
- Bundle properties = new Bundle();
- String manufacturer = usbDevice.getManufacturerName();
- String product = usbDevice.getProductName();
- String version = usbDevice.getVersion();
- String name;
- if (manufacturer == null || manufacturer.isEmpty()) {
- name = product;
- } else if (product == null || product.isEmpty()) {
- name = manufacturer;
- } else {
- name = manufacturer + " " + product;
- }
- properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
- properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
- properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
- properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
- properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
- usbDevice.getSerialNumber());
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, alsaDevice.mCard);
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice);
- properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
- UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
- alsaDevice.mCard, alsaDevice.mDevice);
- if (usbMidiDevice != null) {
- mMidiDevices.put(usbDevice, usbMidiDevice);
- }
- }
+ UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
+ cardRec.getCardNum(), 0 /*device*/);
+ if (usbMidiDevice != null) {
+ mMidiDevices.put(deviceAddress, usbMidiDevice);
}
}
@@ -455,39 +256,28 @@
}
}
- /* package */ void usbDeviceRemoved(UsbDevice usbDevice) {
+ /* package */ void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
if (DEBUG) {
- Slog.d(TAG, "deviceRemoved(): " + usbDevice.getManufacturerName() +
- " " + usbDevice.getProductName());
+ Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")");
}
- UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice);
- Slog.i(TAG, "USB Audio Device Removed: " + audioDevice);
- if (audioDevice != null) {
- if (audioDevice.mHasPlayback || audioDevice.mHasCapture) {
- notifyDeviceState(audioDevice, false /*enabled*/);
+ // Audio
+ UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress);
+ Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
+ if (alsaDevice != null) {
+ if (alsaDevice.hasPlayback() || alsaDevice.hasCapture()) {
+ notifyDeviceState(alsaDevice, false /*enabled*/);
// if there any external devices left, select one of them
selectDefaultDevice();
}
}
- UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice);
- if (usbMidiDevice != null) {
- IoUtils.closeQuietly(usbMidiDevice);
- }
- }
- /* package */ void setAccessoryAudioState(boolean enabled, int card, int device) {
- if (DEBUG) {
- Slog.d(TAG, "setAccessoryAudioState " + enabled + " " + card + " " + device);
- }
- if (enabled) {
- mAccessoryAudioDevice = new UsbAudioDevice(card, device, true, false,
- UsbAudioDevice.kAudioDeviceClass_External);
- notifyDeviceState(mAccessoryAudioDevice, true /*enabled*/);
- } else if (mAccessoryAudioDevice != null) {
- notifyDeviceState(mAccessoryAudioDevice, false /*enabled*/);
- mAccessoryAudioDevice = null;
+ // MIDI
+ UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
+ if (usbMidiDevice != null) {
+ Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
+ IoUtils.closeQuietly(usbMidiDevice);
}
}
@@ -528,31 +318,34 @@
}
*/
- //
- // Logging
- //
- // called by UsbService.dump
- public void dump(IndentingPrintWriter pw) {
- pw.println("Parsers Scan Status:");
- pw.println(" Cards Parser: " + mCardsParser.getScanStatus());
- pw.println(" Devices Parser: " + mDevicesParser.getScanStatus());
- pw.println("USB Audio Devices:");
- for (UsbDevice device : mAudioDevices.keySet()) {
- pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device));
+ /**
+ * Dump the USB alsa state.
+ */
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("cards_parser", UsbAlsaManagerProto.CARDS_PARSER, mCardsParser.getScanStatus());
+
+ for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) {
+ usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
}
- pw.println("USB MIDI Devices:");
- for (UsbDevice device : mMidiDevices.keySet()) {
- pw.println(" " + device.getDeviceName() + ": " + mMidiDevices.get(device));
+
+ for (String deviceAddr : mMidiDevices.keySet()) {
+ // A UsbMidiDevice does not have a handle to the UsbDevice anymore
+ mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
+ UsbAlsaManagerProto.MIDI_DEVICES);
}
+
+ dump.end(token);
}
/*
public void logDevicesList(String title) {
if (DEBUG) {
- for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
Slog.i(TAG, "UsbDevice-------------------");
Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]"));
- Slog.i(TAG, "UsbAudioDevice--------------");
+ Slog.i(TAG, "UsbAlsaDevice--------------");
Slog.i(TAG, "" + entry.getValue());
}
}
@@ -564,7 +357,7 @@
public void logDevices(String title) {
if (DEBUG) {
Slog.i(TAG, title);
- for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
+ for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
Slog.i(TAG, entry.getValue().toShortString());
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbAudioDevice.java b/services/usb/java/com/android/server/usb/UsbAudioDevice.java
deleted file mode 100644
index 4b17dfe..0000000
--- a/services/usb/java/com/android/server/usb/UsbAudioDevice.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2014 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 an
- * limitations under the License.
- */
-
-package com.android.server.usb;
-
-public final class UsbAudioDevice {
- private static final String TAG = "UsbAudioDevice";
- protected static final boolean DEBUG = false;
-
- public final int mCard;
- public final int mDevice;
- public final boolean mHasPlayback;
- public final boolean mHasCapture;
-
- // Device "class" flags
- public static final int kAudioDeviceClassMask = 0x00FFFFFF;
- public static final int kAudioDeviceClass_Undefined = 0x00000000;
- public static final int kAudioDeviceClass_Internal = 0x00000001;
- public static final int kAudioDeviceClass_External = 0x00000002;
- // Device meta-data flags
- public static final int kAudioDeviceMetaMask = 0xFF000000;
- public static final int kAudioDeviceMeta_Alsa = 0x80000000;
- // This member is a combination of the above bit-flags
- public final int mDeviceClass;
-
- private String mDeviceName = "";
- private String mDeviceDescription = "";
-
- public UsbAudioDevice(int card, int device,
- boolean hasPlayback, boolean hasCapture, int deviceClass) {
- mCard = card;
- mDevice = device;
- mHasPlayback = hasPlayback;
- mHasCapture = hasCapture;
- mDeviceClass = deviceClass;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("UsbAudioDevice: [card: " + mCard);
- sb.append(", device: " + mDevice);
- sb.append(", name: " + mDeviceName);
- sb.append(", hasPlayback: " + mHasPlayback);
- sb.append(", hasCapture: " + mHasCapture);
- sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]");
- return sb.toString();
- }
-
- // called by logDevices
- String toShortString() {
- return "[card:" + mCard + " device:" + mDevice + " " + mDeviceName + "]";
- }
-
- String getDeviceName() {
- return mDeviceName;
- }
-
- void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
- mDeviceName = deviceName;
- mDeviceDescription = deviceDescription;
- }
-
-}
-
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index 703f1a1..74d8e12 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
+
import android.app.ActivityManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -35,11 +37,12 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.usb.UsbDebuggingManagerProto;
import android.util.Base64;
import android.util.Slog;
import com.android.internal.R;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
import java.io.File;
@@ -451,21 +454,30 @@
mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
}
- public void dump(IndentingPrintWriter pw) {
- pw.println("USB Debugging State:");
- pw.println(" Connected to adbd: " + (mThread != null));
- pw.println(" Last key received: " + mFingerprints);
- pw.println(" User keys:");
+ /**
+ * Dump the USB debugging state.
+ */
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("connected_to_adb", UsbDebuggingManagerProto.CONNECTED_TO_ADB, mThread != null);
+ writeStringIfNotNull(dump, "last_key_received", UsbDebuggingManagerProto.LAST_KEY_RECEVIED,
+ mFingerprints);
+
try {
- pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
+ dump.write("user_keys", UsbDebuggingManagerProto.USER_KEYS,
+ FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
} catch (IOException e) {
- pw.println("IOException: " + e);
+ Slog.e(TAG, "Cannot read user keys", e);
}
- pw.println(" System keys:");
+
try {
- pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
+ dump.write("system_keys", UsbDebuggingManagerProto.SYSTEM_KEYS,
+ FileUtils.readTextFile(new File("/adb_keys"), 0, null));
} catch (IOException e) {
- pw.println("IOException: " + e);
+ Slog.e(TAG, "Cannot read system keys", e);
}
+
+ dump.end(token);
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 55ffea6..6806947 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,9 @@
package com.android.server.usb;
+import static com.android.internal.usb.DumpUtils.writeAccessory;
+import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
+
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.KeyguardManager;
@@ -41,6 +44,7 @@
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.hardware.usb.gadget.V1_0.IUsbGadget;
import android.hardware.usb.gadget.V1_0.IUsbGadgetCallback;
import android.hardware.usb.gadget.V1_0.Status;
@@ -63,6 +67,8 @@
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.provider.Settings;
+import android.service.usb.UsbDeviceManagerProto;
+import android.service.usb.UsbHandlerProto;
import android.util.Pair;
import android.util.Slog;
@@ -72,7 +78,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.SomeArgs;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -97,6 +103,11 @@
private static final boolean DEBUG = false;
/**
+ * The name of the xml file in which screen unlocked functions are stored.
+ */
+ private static final String USB_PREFS_XML = "UsbDeviceManagerPrefs.xml";
+
+ /**
* The SharedPreference setting per user that stores the screen unlocked functions between
* sessions.
*/
@@ -223,14 +234,12 @@
int userHandle = ActivityManager.getCurrentUser();
boolean secure = mContext.getSystemService(KeyguardManager.class)
.isDeviceSecure(userHandle);
- boolean unlocking = mContext.getSystemService(UserManager.class)
- .isUserUnlockingOrUnlocked(userHandle);
if (DEBUG) {
Slog.v(TAG, "onKeyguardStateChanged: isShowing:" + isShowing + " secure:" + secure
- + " unlocking:" + unlocking + " user:" + userHandle);
+ + " user:" + userHandle);
}
- // We are unlocked when the keyguard is down or non-secure, and user storage is unlocked.
- mHandler.sendMessage(MSG_UPDATE_SCREEN_LOCK, (isShowing && secure) || !unlocking);
+ // We are unlocked when the keyguard is down or non-secure.
+ mHandler.sendMessage(MSG_UPDATE_SCREEN_LOCK, (isShowing && secure));
}
@Override
@@ -494,7 +503,6 @@
mContentResolver = context.getContentResolver();
mCurrentUser = ActivityManager.getCurrentUser();
- mScreenUnlockedFunctions = UsbManager.FUNCTION_NONE;
mScreenLocked = true;
/*
@@ -504,6 +512,16 @@
mAdbEnabled = UsbHandlerLegacy.containsFunction(getSystemProperty(
USB_PERSISTENT_CONFIG_PROPERTY, ""), UsbManager.USB_FUNCTION_ADB);
+ mSettings = getPinnedSharedPrefs(mContext);
+ if (mSettings == null) {
+ Slog.e(TAG, "Couldn't load shared preferences");
+ } else {
+ mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
+ mSettings.getString(
+ String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
+ ""));
+ }
+
// We do not show the USB notification if the primary volume supports mass storage.
// The legacy mass storage UI will be used instead.
final StorageManager storageManager = StorageManager.from(mContext);
@@ -658,11 +676,10 @@
}
protected SharedPreferences getPinnedSharedPrefs(Context context) {
- final File prefsFile = new File(new File(
- Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
- context.getUserId(), context.getPackageName()), "shared_prefs"),
- UsbDeviceManager.class.getSimpleName() + ".xml");
- return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ final File prefsFile = new File(
+ Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), USB_PREFS_XML);
+ return context.createDeviceProtectedStorageContext()
+ .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
}
private boolean isUsbStateChanged(Intent intent) {
@@ -726,35 +743,9 @@
}
private void updateUsbFunctions() {
- updateAudioSourceFunction();
updateMidiFunction();
}
- private void updateAudioSourceFunction() {
- boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_AUDIO_SOURCE) != 0;
- if (enabled != mAudioSourceEnabled) {
- int card = -1;
- int device = -1;
-
- if (enabled) {
- Scanner scanner = null;
- try {
- scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH));
- card = scanner.nextInt();
- device = scanner.nextInt();
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "could not open audio source PCM file", e);
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- }
- mUsbAlsaManager.setAccessoryAudioState(enabled, card, device);
- mAudioSourceEnabled = enabled;
- }
- }
-
private void updateMidiFunction() {
boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
if (enabled != mMidiEnabled) {
@@ -908,11 +899,13 @@
break;
case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS:
mScreenUnlockedFunctions = (Long) msg.obj;
- SharedPreferences.Editor editor = mSettings.edit();
- editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
- mCurrentUser),
- UsbManager.usbFunctionsToString(mScreenUnlockedFunctions));
- editor.commit();
+ if (mSettings != null) {
+ SharedPreferences.Editor editor = mSettings.edit();
+ editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
+ mCurrentUser),
+ UsbManager.usbFunctionsToString(mScreenUnlockedFunctions));
+ editor.commit();
+ }
if (!mScreenLocked && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
// If the screen is unlocked, also set current functions.
setScreenUnlockedFunctions();
@@ -923,14 +916,6 @@
break;
}
mScreenLocked = msg.arg1 == 1;
- if (mSettings == null && !mScreenLocked) {
- // Shared preferences aren't accessible until the user has been unlocked.
- mSettings = getPinnedSharedPrefs(mContext);
- mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
- mSettings.getString(
- String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
- ""));
- }
if (!mBootCompleted) {
break;
}
@@ -984,12 +969,11 @@
}
mCurrentUser = msg.arg1;
mScreenLocked = true;
+ mScreenUnlockedFunctions = UsbManager.FUNCTION_NONE;
if (mSettings != null) {
mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString(
- mSettings.getString(
- String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
- mCurrentUser),
- ""));
+ mSettings.getString(String.format(Locale.ENGLISH,
+ UNLOCKED_CONFIG_PREF, mCurrentUser), ""));
}
setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
}
@@ -1270,32 +1254,66 @@
return mScreenUnlockedFunctions;
}
+ /**
+ * Dump a functions mask either as proto-enums (if dumping to proto) or a string (if dumping
+ * to a print writer)
+ */
+ private void dumpFunctions(DualDumpOutputStream dump, String idName, long id,
+ long functions) {
+ // UsbHandlerProto.UsbFunction matches GadgetFunction
+ for (int i = 0; i < 63; i++) {
+ if ((functions & (1L << i)) != 0) {
+ if (dump.isProto()) {
+ dump.write(idName, id, 1L << i);
+ } else {
+ dump.write(idName, id, GadgetFunction.toString(1L << i));
+ }
+ }
+ }
+ }
- public void dump(IndentingPrintWriter pw) {
- pw.println("USB Device State:");
- pw.println(" mCurrentFunctions: " + mCurrentFunctions);
- pw.println(" mCurrentFunctionsApplied: " + mCurrentFunctionsApplied);
- pw.println(" mScreenUnlockedFunctions: " + mScreenUnlockedFunctions);
- pw.println(" mScreenLocked: " + mScreenLocked);
- pw.println(" mConnected: " + mConnected);
- pw.println(" mConfigured: " + mConfigured);
- pw.println(" mCurrentAccessory: " + mCurrentAccessory);
- pw.println(" mHostConnected: " + mHostConnected);
- pw.println(" mSourcePower: " + mSourcePower);
- pw.println(" mSinkPower: " + mSinkPower);
- pw.println(" mUsbCharging: " + mUsbCharging);
- pw.println(" mHideUsbNotification: " + mHideUsbNotification);
- pw.println(" mAudioAccessoryConnected: " + mAudioAccessoryConnected);
- pw.println(" mAdbEnabled: " + mAdbEnabled);
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dumpFunctions(dump, "current_functions", UsbHandlerProto.CURRENT_FUNCTIONS,
+ mCurrentFunctions);
+ dump.write("current_functions_applied", UsbHandlerProto.CURRENT_FUNCTIONS_APPLIED,
+ mCurrentFunctionsApplied);
+ dumpFunctions(dump, "screen_unlocked_functions",
+ UsbHandlerProto.SCREEN_UNLOCKED_FUNCTIONS, mScreenUnlockedFunctions);
+ dump.write("screen_locked", UsbHandlerProto.SCREEN_LOCKED, mScreenLocked);
+ dump.write("connected", UsbHandlerProto.CONNECTED, mConnected);
+ dump.write("configured", UsbHandlerProto.CONFIGURED, mConfigured);
+ if (mCurrentAccessory != null) {
+ writeAccessory(dump, "current_accessory", UsbHandlerProto.CURRENT_ACCESSORY,
+ mCurrentAccessory);
+ }
+ dump.write("host_connected", UsbHandlerProto.HOST_CONNECTED, mHostConnected);
+ dump.write("source_power", UsbHandlerProto.SOURCE_POWER, mSourcePower);
+ dump.write("sink_power", UsbHandlerProto.SINK_POWER, mSinkPower);
+ dump.write("usb_charging", UsbHandlerProto.USB_CHARGING, mUsbCharging);
+ dump.write("hide_usb_notification", UsbHandlerProto.HIDE_USB_NOTIFICATION,
+ mHideUsbNotification);
+ dump.write("audio_accessory_connected", UsbHandlerProto.AUDIO_ACCESSORY_CONNECTED,
+ mAudioAccessoryConnected);
+ dump.write("adb_enabled", UsbHandlerProto.ADB_ENABLED, mAdbEnabled);
try {
- pw.println(" Kernel state: "
- + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
- pw.println(" Kernel function list: "
- + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
- } catch (IOException e) {
- pw.println("IOException: " + e);
+ writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE,
+ FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not read kernel state", e);
}
+
+ try {
+ writeStringIfNotNull(dump, "kernel_function_list",
+ UsbHandlerProto.KERNEL_FUNCTION_LIST,
+ FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not read kernel function list", e);
+ }
+
+ dump.end(token);
}
/**
@@ -2026,13 +2044,21 @@
}
}
- public void dump(IndentingPrintWriter pw) {
+ /**
+ * Write the state to a dump stream.
+ */
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
if (mHandler != null) {
- mHandler.dump(pw);
+ mHandler.dump(dump, "handler", UsbDeviceManagerProto.HANDLER);
}
if (mDebuggingManager != null) {
- mDebuggingManager.dump(pw);
+ mDebuggingManager.dump(dump, "debugging_manager",
+ UsbDeviceManagerProto.DEBUGGING_MANAGER);
}
+
+ dump.end(token);
}
private native String[] nativeGetAccessoryStrings();
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 7a352a4..0fcd075 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -16,6 +16,10 @@
package com.android.server.usb;
+import static com.android.internal.usb.DumpUtils.writeDevice;
+import static com.android.internal.util.dump.DumpUtils.writeComponentName;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -23,11 +27,16 @@
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.service.ServiceProtoEnums;
+import android.service.usb.UsbConnectionRecordProto;
+import android.service.usb.UsbHostManagerProto;
+import android.service.usb.UsbIsHeadsetProto;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import com.android.server.usb.descriptors.UsbDeviceDescriptor;
@@ -44,7 +53,7 @@
*/
public class UsbHostManager {
private static final String TAG = UsbHostManager.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private final Context mContext;
@@ -84,10 +93,13 @@
long mTimestamp; // Same time-base as system log.
String mDeviceAddress;
- static final int CONNECT = 0;
- static final int CONNECT_BADPARSE = 1;
- static final int CONNECT_BADDEVICE = 2;
- static final int DISCONNECT = -1;
+ static final int CONNECT = ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT; // 0
+ static final int CONNECT_BADPARSE =
+ ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT_BADPARSE; // 1
+ static final int CONNECT_BADDEVICE =
+ ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT_BADDEVICE; // 2
+ static final int DISCONNECT =
+ ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_DISCONNECT; // -1
final int mMode;
final byte[] mDescriptors;
@@ -103,6 +115,31 @@
return (new StringBuilder(sFormat.format(new Date(mTimestamp)))).toString();
}
+ void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("device_address", UsbConnectionRecordProto.DEVICE_ADDRESS, mDeviceAddress);
+ dump.write("mode", UsbConnectionRecordProto.MODE, mMode);
+ dump.write("timestamp", UsbConnectionRecordProto.TIMESTAMP, mTimestamp);
+
+ if (mMode != DISCONNECT) {
+ UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
+
+ UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor();
+
+ dump.write("manufacturer", UsbConnectionRecordProto.MANUFACTURER,
+ deviceDescriptor.getVendorID());
+ dump.write("product", UsbConnectionRecordProto.PRODUCT,
+ deviceDescriptor.getProductID());
+ long isHeadSetToken = dump.start("is_headset", UsbConnectionRecordProto.IS_HEADSET);
+ dump.write("in", UsbIsHeadsetProto.IN, parser.isInputHeadset());
+ dump.write("out", UsbIsHeadsetProto.OUT, parser.isOutputHeadset());
+ dump.end(isHeadSetToken);
+ }
+
+ dump.end(token);
+ }
+
void dumpShort(IndentingPrintWriter pw) {
if (mMode != DISCONNECT) {
pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
@@ -230,6 +267,7 @@
}
private boolean isBlackListed(String deviceAddress) {
+ Slog.i(TAG, "isBlackListed(" + deviceAddress + ")");
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
if (deviceAddress.startsWith(mHostBlacklist[i])) {
@@ -241,6 +279,7 @@
/* returns true if the USB device should not be accessible by applications */
private boolean isBlackListed(int clazz, int subClass) {
+ Slog.i(TAG, "isBlackListed(" + clazz + ", " + subClass + ")");
// blacklist hubs
if (clazz == UsbConstants.USB_CLASS_HUB) return true;
@@ -312,13 +351,7 @@
usbDeviceConnectionHandler);
}
- // Headset?
- boolean isInputHeadset = parser.isInputHeadset();
- boolean isOutputHeadset = parser.isOutputHeadset();
- Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
- + " , out: " + isOutputHeadset + "]");
-
- mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
+ mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
@@ -343,10 +376,13 @@
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
+ if (DEBUG) {
+ Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") - start");
+ }
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
- mUsbAlsaManager.usbDeviceRemoved(device);
+ mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
@@ -395,30 +431,30 @@
/**
* Dump out various information about the state of USB device connections.
- *
*/
- public void dump(IndentingPrintWriter pw, String[] args) {
- pw.println("USB Host State:");
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
synchronized (mHandlerLock) {
if (mUsbDeviceConnectionHandler != null) {
- pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
+ writeComponentName(dump, "default_usb_host_connection_handler",
+ UsbHostManagerProto.DEFAULT_USB_HOST_CONNECTION_HANDLER,
+ mUsbDeviceConnectionHandler);
}
}
synchronized (mLock) {
for (String name : mDevices.keySet()) {
- pw.println(" " + name + ": " + mDevices.get(name));
+ writeDevice(dump, "devices", UsbHostManagerProto.DEVICES, mDevices.get(name));
}
- // Connections
- pw.println("" + mNumConnects + " total connects/disconnects");
- pw.println("Last " + mConnections.size() + " connections/disconnections");
+ dump.write("num_connects", UsbHostManagerProto.NUM_CONNECTS, mNumConnects);
+
for (ConnectionRecord rec : mConnections) {
- rec.dumpShort(pw);
+ rec.dump(dump, "connections", UsbHostManagerProto.CONNECTIONS);
}
-
}
- mUsbAlsaManager.dump(pw);
+ dump.end(token);
}
/**
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index cd19795..f47636e 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -16,14 +16,15 @@
package com.android.server.usb;
+import android.annotation.NonNull;
import android.content.Context;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceServer;
import android.media.midi.MidiDeviceStatus;
import android.media.midi.MidiManager;
import android.media.midi.MidiReceiver;
-import android.media.midi.MidiSender;
import android.os.Bundle;
+import android.service.usb.UsbMidiDeviceProto;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -32,6 +33,7 @@
import com.android.internal.midi.MidiEventScheduler;
import com.android.internal.midi.MidiEventScheduler.MidiEvent;
+import com.android.internal.util.dump.DualDumpOutputStream;
import libcore.io.IoUtils;
@@ -338,6 +340,20 @@
mIsOpen = false;
}
+ /**
+ * Write a description of the device to a dump stream.
+ */
+ public void dump(String deviceAddr, @NonNull DualDumpOutputStream dump, @NonNull String idName,
+ long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("device_address", UsbMidiDeviceProto.DEVICE_ADDRESS, deviceAddr);
+ dump.write("card", UsbMidiDeviceProto.CARD, mAlsaCard);
+ dump.write("device", UsbMidiDeviceProto.DEVICE, mAlsaDevice);
+
+ dump.end(token);
+ }
+
private static native int nativeGetSubdeviceCount(int card, int device);
private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
private native void nativeClose(FileDescriptor[] fileDescriptors);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index e28513a..ddb4f04 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,6 +16,10 @@
package com.android.server.usb;
+import static com.android.internal.usb.DumpUtils.writePort;
+import static com.android.internal.usb.DumpUtils.writePortStatus;
+
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbManager;
@@ -38,12 +42,15 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.usb.UsbPortInfoProto;
+import android.service.usb.UsbPortManagerProto;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
import java.util.ArrayList;
@@ -390,22 +397,22 @@
}
}
- public void dump(IndentingPrintWriter pw) {
- synchronized (mLock) {
- pw.print("USB Port State:");
- if (!mSimulatedPorts.isEmpty()) {
- pw.print(" (simulation active; end with 'dumpsys usb reset')");
- }
- pw.println();
+ /**
+ * Dump the USB port state.
+ */
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
- if (mPorts.isEmpty()) {
- pw.println(" <no ports>");
- } else {
- for (PortInfo portInfo : mPorts.values()) {
- pw.println(" " + portInfo.mUsbPort.getId() + ": " + portInfo);
- }
+ synchronized (mLock) {
+ dump.write("is_simulation_active", UsbPortManagerProto.IS_SIMULATION_ACTIVE,
+ !mSimulatedPorts.isEmpty());
+
+ for (PortInfo portInfo : mPorts.values()) {
+ portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS);
}
}
+
+ dump.end(token);
}
private static class HALCallback extends IUsbCallback.Stub {
@@ -765,6 +772,20 @@
return false;
}
+ void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
+ long token = dump.start(idName, id);
+
+ writePort(dump, "port", UsbPortInfoProto.PORT, mUsbPort);
+ writePortStatus(dump, "status", UsbPortInfoProto.STATUS, mUsbPortStatus);
+ dump.write("can_change_mode", UsbPortInfoProto.CAN_CHANGE_MODE, mCanChangeMode);
+ dump.write("can_change_power_role", UsbPortInfoProto.CAN_CHANGE_POWER_ROLE,
+ mCanChangePowerRole);
+ dump.write("can_change_data_role", UsbPortInfoProto.CAN_CHANGE_DATA_ROLE,
+ mCanChangeDataRole);
+
+ dump.end(token);
+ }
+
@Override
public String toString() {
return "port=" + mUsbPort + ", status=" + mUsbPortStatus
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 5f1f5e4..43f189b 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -41,6 +41,10 @@
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.usb.UsbProfileGroupSettingsManagerProto;
+import android.service.usb.UsbSettingsAccessoryPreferenceProto;
+import android.service.usb.UsbSettingsDevicePreferenceProto;
+import android.service.usb.UserPackageProto;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -52,8 +56,8 @@
import com.android.internal.annotations.Immutable;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
import libcore.io.IoUtils;
@@ -154,6 +158,15 @@
public String toString() {
return user.getIdentifier() + "/" + packageName;
}
+
+ public void dump(DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
+ dump.write("user_id", UserPackageProto.USER_ID, user.getIdentifier());
+ dump.write("package_name", UserPackageProto.PACKAGE_NAME, packageName);
+
+ dump.end(token);
+ }
}
private class MyPackageMonitor extends PackageMonitor {
@@ -301,6 +314,7 @@
* Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
* Should only by called by owner.
*/
+ @GuardedBy("mLock")
private void upgradeSingleUserLocked() {
if (sSingleUserSettingsFile.exists()) {
mDevicePreferenceMap.clear();
@@ -334,6 +348,7 @@
}
}
+ @GuardedBy("mLock")
private void readSettingsLocked() {
if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
@@ -373,6 +388,7 @@
* <p>In the uncommon case that the system crashes in between the scheduling and the write the
* update is lost.</p>
*/
+ @GuardedBy("mLock")
private void scheduleWriteSettingsLocked() {
if (mIsWriteSettingsScheduled) {
return;
@@ -856,6 +872,7 @@
return null;
}
+ @GuardedBy("mLock")
private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
@NonNull DeviceFilter filter) {
ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
@@ -879,6 +896,7 @@
return !keysToRemove.isEmpty();
}
+ @GuardedBy("mLock")
private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
@NonNull AccessoryFilter filter) {
ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
@@ -902,6 +920,7 @@
return !keysToRemove.isEmpty();
}
+ @GuardedBy("mLock")
private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
String metaDataName) {
XmlResourceParser parser = null;
@@ -1109,17 +1128,38 @@
}
}
- public void dump(IndentingPrintWriter pw) {
+ public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
+ long token = dump.start(idName, id);
+
synchronized (mLock) {
- pw.println("Device preferences:");
+ dump.write("parent_user_id", UsbProfileGroupSettingsManagerProto.PARENT_USER_ID,
+ mParentUser.getIdentifier());
+
for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
- pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter));
+ long devicePrefToken = dump.start("device_preferences",
+ UsbProfileGroupSettingsManagerProto.DEVICE_PREFERENCES);
+
+ filter.dump(dump, "filter", UsbSettingsDevicePreferenceProto.FILTER);
+
+ mDevicePreferenceMap.get(filter).dump(dump, "user_package",
+ UsbSettingsDevicePreferenceProto.USER_PACKAGE);
+
+ dump.end(devicePrefToken);
}
- pw.println("Accessory preferences:");
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
- pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter));
+ long accessoryPrefToken = dump.start("accessory_preferences",
+ UsbProfileGroupSettingsManagerProto.ACCESSORY_PREFERENCES);
+
+ filter.dump(dump, "filter", UsbSettingsAccessoryPreferenceProto.FILTER);
+
+ mAccessoryPreferenceMap.get(filter).dump(dump, "user_package",
+ UsbSettingsAccessoryPreferenceProto.USER_PACKAGE);
+
+ dump.end(accessoryPrefToken);
}
}
+
+ dump.end(token);
}
private static Intent createDeviceAttachedIntent(UsbDevice device) {
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 2f6e531..c1a7591 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -37,17 +37,22 @@
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.usb.UsbServiceDumpProto;
+import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.SystemService;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Collections;
/**
* UsbService manages all USB related state, including both host and device support.
@@ -231,6 +236,7 @@
*
* @return Iff the caller is in the current user's profile group
*/
+ @GuardedBy("mLock")
private boolean isCallerInCurrentUserProfileGroupLocked() {
int userIdInt = UserHandle.getCallingUserId();
@@ -503,21 +509,38 @@
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
final long ident = Binder.clearCallingIdentity();
try {
- if (args == null || args.length == 0 || "-a".equals(args[0])) {
- pw.println("USB Manager State:");
- pw.increaseIndent();
+ ArraySet<String> argsSet = new ArraySet<>();
+ Collections.addAll(argsSet, args);
+
+ boolean dumpAsProto = false;
+ if (argsSet.contains("--proto")) {
+ dumpAsProto = true;
+ }
+
+ if (args == null || args.length == 0 || args[0].equals("-a") || dumpAsProto) {
+ DualDumpOutputStream dump;
+ if (dumpAsProto) {
+ dump = new DualDumpOutputStream(new ProtoOutputStream(fd));
+ } else {
+ pw.println("USB MANAGER STATE (dumpsys usb):");
+
+ dump = new DualDumpOutputStream(new IndentingPrintWriter(pw, " "));
+ }
+
if (mDeviceManager != null) {
- mDeviceManager.dump(pw);
+ mDeviceManager.dump(dump, "device_manager", UsbServiceDumpProto.DEVICE_MANAGER);
}
if (mHostManager != null) {
- mHostManager.dump(pw, args);
+ mHostManager.dump(dump, "host_manager", UsbServiceDumpProto.HOST_MANAGER);
}
if (mPortManager != null) {
- mPortManager.dump(pw);
+ mPortManager.dump(dump, "port_manager", UsbServiceDumpProto.PORT_MANAGER);
}
- mAlsaManager.dump(pw);
+ mAlsaManager.dump(dump, "alsa_manager", UsbServiceDumpProto.ALSA_MANAGER);
- mSettingsManager.dump(pw);
+ mSettingsManager.dump(dump, "settings_manager",
+ UsbServiceDumpProto.SETTINGS_MANAGER);
+ dump.flush();
} else if ("set-port-roles".equals(args[0]) && args.length == 4) {
final String portId = args[1];
final int powerRole;
@@ -558,7 +581,8 @@
// during debugging, it might be worth adding a sleep here before
// dumping the new state.
pw.println();
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("add-port".equals(args[0]) && args.length == 3) {
final String portId = args[1];
@@ -583,7 +607,8 @@
if (mPortManager != null) {
mPortManager.addSimulatedPort(portId, supportedModes, pw);
pw.println();
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("connect-port".equals(args[0]) && args.length == 5) {
final String portId = args[1];
@@ -630,31 +655,36 @@
mPortManager.connectSimulatedPort(portId, mode, canChangeMode,
powerRole, canChangePowerRole, dataRole, canChangeDataRole, pw);
pw.println();
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("disconnect-port".equals(args[0]) && args.length == 2) {
final String portId = args[1];
if (mPortManager != null) {
mPortManager.disconnectSimulatedPort(portId, pw);
pw.println();
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("remove-port".equals(args[0]) && args.length == 2) {
final String portId = args[1];
if (mPortManager != null) {
mPortManager.removeSimulatedPort(portId, pw);
pw.println();
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("reset".equals(args[0]) && args.length == 1) {
if (mPortManager != null) {
mPortManager.resetSimulation(pw);
pw.println();
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("ports".equals(args[0]) && args.length == 1) {
if (mPortManager != null) {
- mPortManager.dump(pw);
+ mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")),
+ "", 0);
}
} else if ("dump-descriptors".equals(args[0])) {
mHostManager.dumpDescriptors(pw, args);
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index c7e5998d..caf05a3 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -26,11 +26,12 @@
import android.hardware.usb.UsbManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.usb.UsbSettingsManagerProto;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
/**
* Maintains all {@link UsbUserSettingsManager} for all users.
@@ -134,39 +135,27 @@
/**
* Dump all settings of all users.
- *
- * @param pw The writer to dump to
*/
- void dump(@NonNull IndentingPrintWriter pw) {
+ void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+
synchronized (mSettingsByUser) {
int numUsers = mSettingsByUser.size();
for (int i = 0; i < numUsers; i++) {
- final int userId = mSettingsByUser.keyAt(i);
- final UsbUserSettingsManager settings = mSettingsByUser.valueAt(i);
- pw.println("Settings for user " + userId + ":");
- pw.increaseIndent();
- try {
- settings.dump(pw);
- } finally {
- pw.decreaseIndent();
- }
+ mSettingsByUser.valueAt(i).dump(dump, "user_settings",
+ UsbSettingsManagerProto.USER_SETTINGS);
}
}
synchronized (mSettingsByProfileGroup) {
int numProfileGroups = mSettingsByProfileGroup.size();
for (int i = 0; i < numProfileGroups; i++) {
- final int parentUserId = mSettingsByProfileGroup.keyAt(i);
- final UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.valueAt(i);
- pw.println("Settings for profile group " + parentUserId + ":");
- pw.increaseIndent();
- try {
- settings.dump(pw);
- } finally {
- pw.decreaseIndent();
- }
+ mSettingsByProfileGroup.valueAt(i).dump(dump, "profile_group_settings",
+ UsbSettingsManagerProto.PROFILE_GROUP_SETTINGS);
}
}
+
+ dump.end(token);
}
/**
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 11e43e3..8409506 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -25,17 +25,20 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
+import android.service.usb.UsbSettingsAccessoryPermissionProto;
+import android.service.usb.UsbSettingsDevicePermissionProto;
+import android.service.usb.UsbUserSettingsManagerProto;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
import java.util.HashMap;
@@ -302,28 +305,44 @@
}
}
- public void dump(IndentingPrintWriter pw) {
+ public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
+ long token = dump.start(idName, id);
+
synchronized (mLock) {
- pw.println("Device permissions:");
+ dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
+
for (String deviceName : mDevicePermissionMap.keySet()) {
- pw.print(" " + deviceName + ": ");
+ long devicePermissionToken = dump.start("device_permissions",
+ UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+
+ dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+
SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
int count = uidList.size();
for (int i = 0; i < count; i++) {
- pw.print(Integer.toString(uidList.keyAt(i)) + " ");
+ dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
}
- pw.println();
+
+ dump.end(devicePermissionToken);
}
- pw.println("Accessory permissions:");
for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
- pw.print(" " + accessory + ": ");
+ long accessoryPermissionToken = dump.start("accessory_permissions",
+ UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+
+ dump.write("accessory_description",
+ UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+ accessory.getDescription());
+
SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
int count = uidList.size();
for (int i = 0; i < count; i++) {
- pw.print(Integer.toString(uidList.keyAt(i)) + " ");
+ dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
}
- pw.println();
+
+ dump.end(accessoryPermissionToken);
}
}
+
+ dump.end(token);
}
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 7a1e9e2..297a6ea 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private final String mDeviceAddr;
@@ -358,6 +358,139 @@
return list;
}
+ /*
+ * Attribute predicates
+ */
+ /**
+ * @hide
+ */
+ public boolean hasInput() {
+ if (DEBUG) {
+ Log.d(TAG, "---- hasInput()");
+ }
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ boolean hasInput = false;
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ UsbACTerminal inDescr = (UsbACTerminal) descriptor;
+ // Check for input and bi-directional terminal types
+ int type = inDescr.getTerminalType();
+ if (DEBUG) {
+ Log.d(TAG, " type:0x" + Integer.toHexString(type));
+ }
+ if ((type >= UsbTerminalTypes.TERMINAL_IN_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY)
+ || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)
+ || (type == UsbTerminalTypes.TERMINAL_USB_STREAMING)) {
+ hasInput = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "hasInput() = " + hasInput);
+ }
+ return hasInput;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasOutput() {
+ if (DEBUG) {
+ Log.d(TAG, "---- hasOutput()");
+ }
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ boolean hasOutput = false;
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ UsbACTerminal outDescr = (UsbACTerminal) descriptor;
+ // Check for output and bi-directional terminal types
+ int type = outDescr.getTerminalType();
+ if (DEBUG) {
+ Log.d(TAG, " type:0x" + Integer.toHexString(type));
+ }
+ if ((type >= UsbTerminalTypes.TERMINAL_OUT_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER)
+ || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)) {
+ hasOutput = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "hasOutput() = " + hasOutput);
+ }
+ return hasOutput;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasMic() {
+ boolean hasMic = false;
+
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ UsbACTerminal inDescr = (UsbACTerminal) descriptor;
+ if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
+ hasMic = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ return hasMic;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasSpeaker() {
+ boolean hasSpeaker = false;
+
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ UsbACTerminal outDescr = (UsbACTerminal) descriptor;
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
+ hasSpeaker = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ return hasSpeaker;
+ }
+
/**
* @hide
*/
@@ -397,48 +530,12 @@
}
float probability = 0.0f;
- ArrayList<UsbDescriptor> acDescriptors;
// Look for a microphone
- boolean hasMic = false;
- acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
- UsbACInterface.AUDIO_AUDIOCONTROL);
- for (UsbDescriptor descriptor : acDescriptors) {
- if (descriptor instanceof UsbACTerminal) {
- UsbACTerminal inDescr = (UsbACTerminal) descriptor;
- if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
- || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
- || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
- || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
- hasMic = true;
- break;
- }
- } else {
- Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
- + " t:0x" + Integer.toHexString(descriptor.getType()));
- }
- }
+ boolean hasMic = hasMic();
// Look for a "speaker"
- boolean hasSpeaker = false;
- acDescriptors =
- getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
- UsbACInterface.AUDIO_AUDIOCONTROL);
- for (UsbDescriptor descriptor : acDescriptors) {
- if (descriptor instanceof UsbACTerminal) {
- UsbACTerminal outDescr = (UsbACTerminal) descriptor;
- if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
- || outDescr.getTerminalType()
- == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
- || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
- hasSpeaker = true;
- break;
- }
- } else {
- Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
- + " t:0x" + Integer.toHexString(descriptor.getType()));
- }
- }
+ boolean hasSpeaker = hasSpeaker();
if (hasMic && hasSpeaker) {
probability += 0.75f;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 6799417..8c18518 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -352,8 +352,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x01000000
+ // Next CAPABILITY value: 0x02000000
//******************************************************************************************
/**
@@ -528,6 +531,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(" CAPABILITY_CAN_PULL_CALL");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(" CAPABILITY_SUPPORT_DEFLECT");
+ }
builder.append("]");
return builder.toString();
}
@@ -1235,6 +1241,15 @@
}
/**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to deflect.
+ *
+ * @param address The address to which the call will be deflected.
+ */
+ public void deflect(Uri address) {
+ mInCallAdapter.deflectCall(mTelecomCallId, address);
+ }
+
+ /**
* Instructs this {@link #STATE_RINGING} {@code Call} to reject.
*
* @param rejectWithMessage Whether to reject with a text message.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 63f970a..24184e0 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -328,8 +328,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x02000000
+ // Next CAPABILITY value: 0x04000000
//**********************************************************************************************
/**
@@ -726,6 +729,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
+ }
builder.append("]");
return builder.toString();
@@ -787,6 +793,10 @@
builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
}
+ if (can(properties, PROPERTY_IS_RTT)) {
+ builder.append(isLong ? " PROPERTY_IS_RTT" : " rtt");
+ }
+
builder.append("]");
return builder.toString();
}
@@ -2646,6 +2656,7 @@
* side of the coll.
*/
public final void sendRttSessionRemotelyTerminated() {
+ unsetRttProperty();
mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
}
@@ -2747,6 +2758,12 @@
/**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to deflect.
+ */
+ public void onDeflect(Uri address) {}
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to reject.
*/
public void onReject() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index c1040ad..211699e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -124,6 +124,7 @@
private static final String SESSION_ABORT = "CS.ab";
private static final String SESSION_ANSWER = "CS.an";
private static final String SESSION_ANSWER_VIDEO = "CS.anV";
+ private static final String SESSION_DEFLECT = "CS.def";
private static final String SESSION_REJECT = "CS.r";
private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
private static final String SESSION_SILENCE = "CS.s";
@@ -181,6 +182,7 @@
private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
private static final int MSG_HANDOVER_FAILED = 32;
private static final int MSG_HANDOVER_COMPLETE = 33;
+ private static final int MSG_DEFLECT = 34;
private static Connection sNullConnection;
@@ -353,6 +355,20 @@
}
@Override
+ public void deflect(String callId, Uri address, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_DEFLECT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = address;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void reject(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_REJECT);
try {
@@ -847,6 +863,17 @@
}
break;
}
+ case MSG_DEFLECT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT);
+ try {
+ deflect((String) args.arg1, (Uri) args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_REJECT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
@@ -1605,6 +1632,11 @@
findConnectionForAction(callId, "answer").onAnswer();
}
+ private void deflect(String callId, Uri address) {
+ Log.d(this, "deflect %s", callId);
+ findConnectionForAction(callId, "deflect").onDeflect(address);
+ }
+
private void reject(String callId) {
Log.d(this, "reject %s", callId);
findConnectionForAction(callId, "reject").onReject();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 658685f..8678e33 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.net.Uri;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -61,6 +62,19 @@
}
/**
+ * Instructs Telecom to deflect the specified call.
+ *
+ * @param callId The identifier of the call to deflect.
+ * @param address The address to deflect.
+ */
+ public void deflectCall(String callId, Uri address) {
+ try {
+ mAdapter.deflectCall(callId, address);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to reject the specified call.
*
* @param callId The identifier of the call to reject.
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index fcfc593..95eb14a 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -134,6 +134,25 @@
"android.telecom.extra.LOG_SELF_MANAGED_CALLS";
/**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone"
+ * when the user is recording audio on the device.
+ * <p>
+ * The call recording tone is played over the telephony audio stream so that the remote party
+ * has an audible indication that it is possible their call is being recorded using a call
+ * recording app on the device.
+ * <p>
+ * This extra only has an effect for calls placed via Telephony (e.g.
+ * {@link #CAPABILITY_SIM_SUBSCRIPTION}).
+ * <p>
+ * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is
+ * in progress.
+ * @hide
+ */
+ public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
+ "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 3a84f00..e35093c 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
@@ -58,6 +59,8 @@
void answer(String callId, in Session.Info sessionInfo);
+ void deflect(String callId, in Uri address, in Session.Info sessionInfo);
+
void reject(String callId, in Session.Info sessionInfo);
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 87ccd3e..57df5c1 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
@@ -29,6 +30,8 @@
oneway interface IInCallAdapter {
void answerCall(String callId, int videoState);
+ void deflectCall(String callId, in Uri address);
+
void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
void disconnectCall(String callId);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a34e9f9..c8c898a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -77,6 +77,14 @@
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ /**
+ * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+ "call_barring_visibility_bool";
+
/**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
@@ -146,6 +154,15 @@
public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
/**
+ * Determines if the carrier requires that a tone be played to the remote party when an app is
+ * recording audio during a call (e.g. using a call recording app).
+ * <p>
+ * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
+ * in order to work.
+ * @hide
+ */
+ public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+ /**
* Determines if the carrier requires converting the destination number before sending out an
* SMS. Certain networks and numbering plans require different formats.
*/
@@ -503,6 +520,20 @@
public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL
= "carrier_volte_override_wfc_provisioning_bool";
+ /**
+ * Override the device's configuration for the cellular data service to use for this SIM card.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_data_service_wwan_package_override_string";
+
+ /**
+ * Override the device's configuration for the IWLAN data service to use for this SIM card.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_data_service_wlan_package_override_string";
+
/** Flag specifying whether VoLTE TTY is supported. */
public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
= "carrier_volte_tty_supported_bool";
@@ -1345,6 +1376,12 @@
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+ /**
+ * Flag indicating whether the carrier supports call deflection for an incoming IMS call.
+ * @hide
+ */
+ public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+ "carrier_allow_deflect_ims_call_bool";
/**
* Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
@@ -1381,6 +1418,14 @@
public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
/**
+ * When true, indicates that the HD audio icon in the in-call screen should be shown for
+ * GSM/CDMA calls.
+ * @hide
+ */
+ public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO =
+ "gsm_cdma_calls_can_be_hd_audio";
+
+ /**
* Whether system apps are allowed to use fallback if carrier video call is not available.
* Defaults to {@code true}.
*
@@ -1783,16 +1828,34 @@
public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
"lte_rsrp_thresholds_int_array";
+ /**
+ * Decides when clients try to bind to iwlan network service, which package name will
+ * the binding intent go to.
+ * @hide
+ */
+ public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_network_service_wlan_package_override_string";
+
+ /**
+ * Decides when clients try to bind to wwan (cellular) network service, which package name will
+ * the binding intent go to.
+ * @hide
+ */
+ public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_network_service_wwan_package_override_string";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
static {
sDefaults = new PersistableBundle();
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+ sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
@@ -1820,6 +1883,10 @@
sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+ sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
@@ -1833,6 +1900,7 @@
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+ sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -2033,6 +2101,7 @@
sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index dfaaab9..ece1ee3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -36,48 +36,14 @@
private int mEvdoEcio; // This value is the EVDO Ec/Io
private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
int evdoSnr) {
- initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param cdmaDbm
- * @param cdmaEcio
- * @param evdoDbm
- * @param evdoEcio
- * @param evdoSnr
- *
- * @hide
- */
- public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
mCdmaDbm = cdmaDbm;
mCdmaEcio = cdmaEcio;
mEvdoDbm = evdoDbm;
@@ -85,9 +51,12 @@
mEvdoSnr = evdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthCdma s) {
mCdmaDbm = s.mCdmaDbm;
mCdmaEcio = s.mCdmaEcio;
@@ -96,9 +65,7 @@
mEvdoSnr = s.mEvdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthCdma copy() {
return new CellSignalStrengthCdma(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index f68d2ca..8687cd1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -34,80 +34,40 @@
private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- private int mTimingAdvance;
+ private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm(int ss, int ber) {
- initialize(ss, ber);
+ this(ss, ber, Integer.MAX_VALUE);
}
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mTimingAdvance = Integer.MAX_VALUE;
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- * @param ta timing advance
- *
- * @hide
- */
- public void initialize(int ss, int ber, int ta) {
+ /** @hide */
+ public CellSignalStrengthGsm(int ss, int ber, int ta) {
mSignalStrength = ss;
mBitErrorRate = ber;
mTimingAdvance = ta;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthGsm s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthGsm copy() {
return new CellSignalStrengthGsm(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 6ffc8b6..f009fb1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -37,50 +37,15 @@
private int mCqi;
private int mTimingAdvance;
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
int timingAdvance) {
- initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthLte(CellSignalStrengthLte s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param lteSignalStrength
- * @param rsrp
- * @param rsrq
- * @param rssnr
- * @param cqi
- *
- * @hide
- */
- public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
- int timingAdvance) {
- mSignalStrength = lteSignalStrength;
+ mSignalStrength = signalStrength;
mRsrp = rsrp;
mRsrq = rsrq;
mRssnr = rssnr;
@@ -88,25 +53,12 @@
mTimingAdvance = timingAdvance;
}
- /**
- * Initialize from the SignalStrength structure.
- *
- * @param ss
- *
- * @hide
- */
- public void initialize(SignalStrength ss, int timingAdvance) {
- mSignalStrength = ss.getLteSignalStrength();
- mRsrp = ss.getLteRsrp();
- mRsrq = ss.getLteRsrq();
- mRssnr = ss.getLteRssnr();
- mCqi = ss.getLteCqi();
- mTimingAdvance = timingAdvance;
+ /** @hide */
+ public CellSignalStrengthLte(CellSignalStrengthLte s) {
+ copyFrom(s);
}
- /**
- * @hide
- */
+ /** @hide */
protected void copyFrom(CellSignalStrengthLte s) {
mSignalStrength = s.mSignalStrength;
mRsrp = s.mRsrp;
@@ -116,9 +68,7 @@
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthLte copy() {
return new CellSignalStrengthLte(this);
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 2cd56b8..dd32a96 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -34,62 +34,32 @@
private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
- private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma(int ss, int ber) {
- initialize(ss, ber);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
mSignalStrength = ss;
mBitErrorRate = ber;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthWcdma s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthWcdma copy() {
return new CellSignalStrengthWcdma(this);
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 059a2d0..da3d87b 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -437,6 +437,7 @@
int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, null);
+ return;
}
} catch (RemoteException e) {
mService.set(null);
@@ -686,30 +687,42 @@
}
/**
- * Gets information about the status of a file pending download.
+ * Requests information about the state of a file pending download.
*
- * If there was a problem communicating with the middleware or if it has no records of the
+ * The state will be delivered as a callback via
+ * {@link DownloadStateCallback#onStateUpdated(DownloadRequest, FileInfo, int)}. If no such
+ * callback has been registered via
+ * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}, this
+ * method will be a no-op.
+ *
+ * If the middleware has no record of the
* file indicated by {@code fileInfo} being associated with {@code downloadRequest},
- * {@link #STATUS_UNKNOWN} will be returned.
+ * an {@link IllegalArgumentException} will be thrown.
*
* @param downloadRequest The download request to query.
* @param fileInfo The particular file within the request to get information on.
- * @return The status of the download.
*/
- @DownloadStatus
- public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
+ public void requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
}
try {
- return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+ int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO) {
+ throw new IllegalArgumentException("Unknown file.");
+ }
+ sendErrorToApp(result, null);
+ }
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
- return STATUS_UNKNOWN;
}
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index 4f137be..bba779d 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -298,9 +298,9 @@
&& mEmergencyOnly == other.mEmergencyOnly
&& (mAvailableServices == other.mAvailableServices
|| Arrays.equals(mAvailableServices, other.mAvailableServices))
- && mCellIdentity == other.mCellIdentity
- && mVoiceSpecificStates == other.mVoiceSpecificStates
- && mDataSpecificStates == other.mDataSpecificStates;
+ && equals(mCellIdentity, other.mCellIdentity)
+ && equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
+ && equals(mDataSpecificStates, other.mDataSpecificStates);
}
@Override
@@ -329,4 +329,14 @@
return new NetworkRegistrationState[size];
}
};
+
+ private static boolean equals(Object o1, Object o2) {
+ if (o1 == o2) {
+ return true;
+ } else if (o1 == null) {
+ return false;
+ } else {
+ return o1.equals(o2);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 94921de..35682a7 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -28,6 +28,8 @@
import android.os.RemoteException;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
@@ -38,7 +40,7 @@
* follow the following format:
* ...
* <service android:name=".xxxNetworkService"
- * android:permission="android.permission.BIND_NETWORK_SERVICE" >
+ * android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
* <intent-filter>
* <action android:name="android.telephony.NetworkService" />
* </intent-filter>
@@ -68,7 +70,11 @@
private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
- private final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
/**
* The abstract class of the actual network service implementation. The network service provider
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index ff67116..fadfc91 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -22,6 +22,7 @@
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.android.i18n.phonenumbers.ShortNumberInfo;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -42,6 +43,8 @@
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -49,8 +52,31 @@
/**
* Various utilities for dealing with phone number strings.
*/
-public class PhoneNumberUtils
-{
+public class PhoneNumberUtils {
+ /** {@hide} */
+ @IntDef(prefix = "BCD_EXTENDED_TYPE_", value = {
+ BCD_EXTENDED_TYPE_EF_ADN,
+ BCD_EXTENDED_TYPE_CALLED_PARTY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BcdExtendType {}
+
+ /*
+ * The BCD extended type used to determine the extended char for the digit which is greater than
+ * 9.
+ *
+ * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
+ */
+ public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
+
+ /*
+ * The BCD extended type used to determine the extended char for the digit which is greater than
+ * 9.
+ *
+ * see TS 24.008 section 10.5.4.7 Called party BCD number
+ */
+ public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
+
/*
* Special characters
*
@@ -77,22 +103,6 @@
public static final int TOA_International = 0x91;
public static final int TOA_Unknown = 0x81;
- /*
- * The BCD extended type used to determine the extended char for the digit which is greater than
- * 9.
- *
- * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
- */
- public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
-
- /*
- * The BCD extended type used to determine the extended char for the digit which is greater than
- * 9.
- *
- * see TS 24.008 section 10.5.4.7 Called party BCD number
- */
- public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
-
static final String LOG_TAG = "PhoneNumberUtils";
private static final boolean DBG = false;
@@ -844,7 +854,7 @@
*
*/
public static String calledPartyBCDToString(
- byte[] bytes, int offset, int length, int bcdExtType) {
+ byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
boolean prependPlus = false;
StringBuilder ret = new StringBuilder(1 + length * 2);
@@ -944,7 +954,8 @@
}
private static void internalCalledPartyBCDFragmentToString(
- StringBuilder sb, byte [] bytes, int offset, int length, int bcdExtType) {
+ StringBuilder sb, byte [] bytes, int offset, int length,
+ @BcdExtendType int bcdExtType) {
for (int i = offset ; i < length + offset ; i++) {
byte b;
char c;
@@ -999,7 +1010,7 @@
* TOA byte. For example: SIM ADN extension fields
*/
public static String calledPartyBCDFragmentToString(
- byte[] bytes, int offset, int length, int bcdExtType) {
+ byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
StringBuilder ret = new StringBuilder(length * 2);
internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
return ret.toString();
@@ -1009,7 +1020,7 @@
* Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
* invalid code.
*/
- private static char bcdToChar(byte b, int bcdExtType) {
+ private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
if (b < 0xa) {
return (char) ('0' + b);
}
@@ -1027,7 +1038,7 @@
return extended.charAt(b - 0xa);
}
- private static int charToBCD(char c, int bcdExtType) {
+ private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
if ('0' <= c && c <= '9') {
return c - '0';
}
@@ -1134,7 +1145,7 @@
*
* @return BCD byte array
*/
- public static byte[] numberToCalledPartyBCD(String number, int bcdExtType) {
+ public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
return numberToCalledPartyBCDHelper(number, false, bcdExtType);
}
@@ -1143,7 +1154,7 @@
* the return array.
*/
private static byte[] numberToCalledPartyBCDHelper(
- String number, boolean includeLength, int bcdExtType) {
+ String number, boolean includeLength, @BcdExtendType int bcdExtType) {
int numberLenReal = number.length();
int numberLenEffective = numberLenReal;
boolean hasPlus = number.indexOf('+') != -1;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 90a3677..cb867ab 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -22,13 +22,13 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -94,47 +94,6 @@
*/
public static final int DUPLEX_MODE_TDD = 2;
- /**
- * RIL level registration state values from ril.h
- * ((const char **)response)[0] is registration state 0-6,
- * 0 - Not registered, MT is not currently searching
- * a new operator to register
- * 1 - Registered, home network
- * 2 - Not registered, but MT is currently searching
- * a new operator to register
- * 3 - Registration denied
- * 4 - Unknown
- * 5 - Registered, roaming
- * 10 - Same as 0, but indicates that emergency calls
- * are enabled.
- * 12 - Same as 2, but indicates that emergency calls
- * are enabled.
- * 13 - Same as 3, but indicates that emergency calls
- * are enabled.
- * 14 - Same as 4, but indicates that emergency calls
- * are enabled.
- * @hide
- */
- public static final int RIL_REG_STATE_NOT_REG = 0;
- /** @hide */
- public static final int RIL_REG_STATE_HOME = 1;
- /** @hide */
- public static final int RIL_REG_STATE_SEARCHING = 2;
- /** @hide */
- public static final int RIL_REG_STATE_DENIED = 3;
- /** @hide */
- public static final int RIL_REG_STATE_UNKNOWN = 4;
- /** @hide */
- public static final int RIL_REG_STATE_ROAMING = 5;
- /** @hide */
- public static final int RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED = 10;
- /** @hide */
- public static final int RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED = 12;
- /** @hide */
- public static final int RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED = 13;
- /** @hide */
- public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "RIL_RADIO_TECHNOLOGY_" },
@@ -233,22 +192,6 @@
| (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
| (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
- /**
- * Available registration states for GSM, UMTS and CDMA.
- */
- /** @hide */
- public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0;
- /** @hide */
- public static final int REGISTRATION_STATE_HOME_NETWORK = 1;
- /** @hide */
- public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2;
- /** @hide */
- public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3;
- /** @hide */
- public static final int REGISTRATION_STATE_UNKNOWN = 4;
- /** @hide */
- public static final int REGISTRATION_STATE_ROAMING = 5;
-
private int mVoiceRegState = STATE_OUT_OF_SERVICE;
private int mDataRegState = STATE_OUT_OF_SERVICE;
@@ -1340,6 +1283,84 @@
}
/** @hide */
+ public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) {
+ switch(rt) {
+ case RIL_RADIO_TECHNOLOGY_GPRS:
+ case RIL_RADIO_TECHNOLOGY_EDGE:
+ case RIL_RADIO_TECHNOLOGY_GSM:
+ return AccessNetworkType.GERAN;
+ case RIL_RADIO_TECHNOLOGY_UMTS:
+ case RIL_RADIO_TECHNOLOGY_HSDPA:
+ case RIL_RADIO_TECHNOLOGY_HSPAP:
+ case RIL_RADIO_TECHNOLOGY_HSUPA:
+ case RIL_RADIO_TECHNOLOGY_HSPA:
+ case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return AccessNetworkType.UTRAN;
+ case RIL_RADIO_TECHNOLOGY_IS95A:
+ case RIL_RADIO_TECHNOLOGY_IS95B:
+ case RIL_RADIO_TECHNOLOGY_1xRTT:
+ case RIL_RADIO_TECHNOLOGY_EVDO_0:
+ case RIL_RADIO_TECHNOLOGY_EVDO_A:
+ case RIL_RADIO_TECHNOLOGY_EVDO_B:
+ case RIL_RADIO_TECHNOLOGY_EHRPD:
+ return AccessNetworkType.CDMA2000;
+ case RIL_RADIO_TECHNOLOGY_LTE:
+ case RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return AccessNetworkType.EUTRAN;
+ case RIL_RADIO_TECHNOLOGY_IWLAN:
+ return AccessNetworkType.IWLAN;
+ case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+ default:
+ return AccessNetworkType.UNKNOWN;
+ }
+ }
+
+ /** @hide */
+ public static int networkTypeToRilRadioTechnology(int networkType) {
+ switch(networkType) {
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS;
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE;
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA;
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA;
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0;
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A;
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B;
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_GSM;
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+ case TelephonyManager.NETWORK_TYPE_IWLAN:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
+ default:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ }
+ }
+
+
+ /** @hide */
public int getDataNetworkType() {
return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5c290da..03a8f33 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6587,11 +6587,48 @@
* @hide
*/
public void setBasebandVersionForPhone(int phoneId, String version) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, version);
+ }
+
+ /**
+ * Get baseband version for the default phone.
+ *
+ * @return baseband version.
+ * @hide
+ */
+ public String getBasebandVersion() {
+ int phoneId = getPhoneId();
+ return getBasebandVersionForPhone(phoneId);
+ }
+
+ /**
+ * Get baseband version for the default phone using the legacy approach.
+ * This change was added in P, to ensure backward compatiblity.
+ *
+ * @return baseband version.
+ * @hide
+ */
+ private String getBasebandVersionLegacy(int phoneId) {
if (SubscriptionManager.isValidPhoneId(phoneId)) {
String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
((phoneId == 0) ? "" : Integer.toString(phoneId));
- SystemProperties.set(prop, version);
+ return SystemProperties.get(prop);
}
+ return null;
+ }
+
+ /**
+ * Get baseband version by phone id.
+ *
+ * @return baseband version.
+ * @hide
+ */
+ public String getBasebandVersionForPhone(int phoneId) {
+ String version = getBasebandVersionLegacy(phoneId);
+ if (version != null && !version.isEmpty()) {
+ setBasebandVersionForPhone(phoneId, version);
+ }
+ return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, "");
}
/**
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 0b3cbad..0c17147 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -55,10 +55,11 @@
/** Card state restricted. */
public static final int CARD_STATE_INFO_RESTRICTED = 4;
- public final boolean isActive;
- public final boolean isEuicc;
- public final String cardId;
- public final @CardStateInfo int cardStateInfo;
+ private final boolean mIsActive;
+ private final boolean mIsEuicc;
+ private final String mCardId;
+ private final @CardStateInfo int mCardStateInfo;
+ private final int mLogicalSlotIdx;
public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
@Override
@@ -73,18 +74,20 @@
};
private UiccSlotInfo(Parcel in) {
- isActive = in.readByte() != 0;
- isEuicc = in.readByte() != 0;
- cardId = in.readString();
- cardStateInfo = in.readInt();
+ mIsActive = in.readByte() != 0;
+ mIsEuicc = in.readByte() != 0;
+ mCardId = in.readString();
+ mCardStateInfo = in.readInt();
+ mLogicalSlotIdx = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (isActive ? 1 : 0));
- dest.writeByte((byte) (isEuicc ? 1 : 0));
- dest.writeString(cardId);
- dest.writeInt(cardStateInfo);
+ dest.writeByte((byte) (mIsActive ? 1 : 0));
+ dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+ dest.writeString(mCardId);
+ dest.writeInt(mCardStateInfo);
+ dest.writeInt(mLogicalSlotIdx);
}
@Override
@@ -93,28 +96,33 @@
}
public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
- @CardStateInfo int cardStateInfo) {
- this.isActive = isActive;
- this.isEuicc = isEuicc;
- this.cardId = cardId;
- this.cardStateInfo = cardStateInfo;
+ @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+ this.mIsActive = isActive;
+ this.mIsEuicc = isEuicc;
+ this.mCardId = cardId;
+ this.mCardStateInfo = cardStateInfo;
+ this.mLogicalSlotIdx = logicalSlotIdx;
}
public boolean getIsActive() {
- return isActive;
+ return mIsActive;
}
public boolean getIsEuicc() {
- return isEuicc;
+ return mIsEuicc;
}
public String getCardId() {
- return cardId;
+ return mCardId;
}
@CardStateInfo
public int getCardStateInfo() {
- return cardStateInfo;
+ return mCardStateInfo;
+ }
+
+ public int getLogicalSlotIdx() {
+ return mLogicalSlotIdx;
}
@Override
@@ -127,32 +135,36 @@
}
UiccSlotInfo that = (UiccSlotInfo) obj;
- return (isActive == that.isActive)
- && (isEuicc == that.isEuicc)
- && (cardId == that.cardId)
- && (cardStateInfo == that.cardStateInfo);
+ return (mIsActive == that.mIsActive)
+ && (mIsEuicc == that.mIsEuicc)
+ && (mCardId == that.mCardId)
+ && (mCardStateInfo == that.mCardStateInfo)
+ && (mLogicalSlotIdx == that.mLogicalSlotIdx);
}
@Override
public int hashCode() {
int result = 1;
- result = 31 * result + (isActive ? 1 : 0);
- result = 31 * result + (isEuicc ? 1 : 0);
- result = 31 * result + Objects.hashCode(cardId);
- result = 31 * result + cardStateInfo;
+ result = 31 * result + (mIsActive ? 1 : 0);
+ result = 31 * result + (mIsEuicc ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mCardId);
+ result = 31 * result + mCardStateInfo;
+ result = 31 * result + mLogicalSlotIdx;
return result;
}
@Override
public String toString() {
- return "UiccSlotInfo (isActive="
- + isActive
- + ", isEuicc="
- + isEuicc
- + ", cardId="
- + cardId
+ return "UiccSlotInfo (mIsActive="
+ + mIsActive
+ + ", mIsEuicc="
+ + mIsEuicc
+ + ", mCardId="
+ + mCardId
+ ", cardState="
- + cardStateInfo
+ + mCardStateInfo
+ + ", phoneId="
+ + mLogicalSlotIdx
+ ")";
}
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 63e8c3b..e8c1cb1 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -34,6 +34,8 @@
import android.telephony.Rlog;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -105,7 +107,9 @@
private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
- private final IBinder mBinder = new IDataServiceWrapper();
+ /** @hide */
+ @VisibleForTesting
+ public final IDataServiceWrapper mBinder = new IDataServiceWrapper();
/**
* The abstract class of the actual data service implementation. The data service provider
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index c3d103f..a20d4f5 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -754,6 +754,22 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param number number to be deflected to
+ */
+ public void deflect(String number) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.deflect(number);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 00cb1e2..e5ed825 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -182,6 +182,15 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ @Override
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index c6ca6fd..7b9fe2b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -174,6 +174,11 @@
}
@Override
+ public void deflect(String deflectNumber) {
+ ImsCallSessionImplBase.this.deflect(deflectNumber);
+ }
+
+ @Override
public void reject(int reason) {
ImsCallSessionImplBase.this.reject(reason);
}
@@ -395,6 +400,14 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index af0af24..75ca35e 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -128,6 +128,9 @@
/** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */
public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
+
+ /** Indicates the the middleware has no record of the supplied {@link FileInfo} */
+ public static final int ERROR_UNKNOWN_FILE_INFO = 403;
}
private MbmsErrors() {}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index cb93542..7d9845f 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -46,7 +46,7 @@
int cancelDownload(in DownloadRequest downloadRequest);
- int getDownloadStatus(in DownloadRequest downloadRequest, in FileInfo fileInfo);
+ int requestDownloadState(in DownloadRequest downloadRequest, in FileInfo fileInfo);
int resetDownloadKnowledge(in DownloadRequest downloadRequest);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 4fee3df..86b1b7a 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -370,18 +370,18 @@
}
/**
- * Gets information about the status of a file pending download.
+ * Requests information about the state of a file pending download.
*
- * If the middleware has not yet been properly initialized or if it has no records of the
+ * If the middleware has no records of the
* file indicated by {@code fileInfo} being associated with {@code downloadRequest},
- * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_FILE_INFO} must be returned.
*
* @param downloadRequest The download request to query.
* @param fileInfo The particular file within the request to get information on.
- * @return The status of the download.
+ * @return {@link MbmsErrors#SUCCESS} if the request was successful, an error code otherwise.
*/
@Override
- public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+ public int requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo)
throws RemoteException {
return 0;
}
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 203e6cf..15234e5 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -136,6 +136,13 @@
void accept(int callType, in ImsStreamMediaProfile profile);
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ void deflect(String deflectNumber);
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 91032f3..d999c13 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -108,6 +108,7 @@
public static final int EVENT_SET_CARRIER_DATA_ENABLED = BASE + 46;
public static final int EVENT_DATA_RECONNECT = BASE + 47;
public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
+ public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cdee9e6..a3a3080 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -220,11 +220,6 @@
String SETUP_DATA_PROTOCOL_IPV6 = "IPV6";
String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6";
- /* Deactivate data call reasons */
- int DEACTIVATE_REASON_NONE = 0;
- int DEACTIVATE_REASON_RADIO_OFF = 1;
- int DEACTIVATE_REASON_PDP_RESET = 2;
-
/* NV config radio reset types. */
int NV_CONFIG_RELOAD_RESET = 1;
int NV_CONFIG_ERASE_RESET = 2;
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 62fed61..b65cda9 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -24,6 +24,9 @@
srcs: ["src/**/*.java"],
+ // Needs to be consistent with the repackaged version of this make target.
+ java_version: "1.8",
+
no_framework_libs: true,
hostdex: true,
libs: [
@@ -55,21 +58,22 @@
name: "repackaged.android.test.base",
static_libs: ["android.test.base"],
-
no_framework_libs: true,
libs: [
"framework",
],
jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
}
// Build the android.test.base-minus-junit library
// ===============================================
// This contains the android.test classes from android.test.base plus
// the com.android.internal.util.Predicate[s] classes. This is only
-// intended for inclusion in the android.test.legacy static library and
-// must not be used elsewhere.
+// intended for inclusion in the android.test.legacy and
+// legacy-android-test static libraries and must not be used elsewhere.
java_library_static {
name: "android.test.base-minus-junit",
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index b1ae40e..54e07a16 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -19,6 +19,8 @@
java_library {
name: "android.test.mock",
+ // Needs to be consistent with the repackaged version of this make target.
+ java_version: "1.8",
srcs: ["src/**/*.java"],
no_framework_libs: true,
@@ -35,4 +37,6 @@
static_libs: ["android.test.mock"],
jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index dfaeed5..66b9527 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -19,6 +19,8 @@
java_library {
name: "android.test.runner",
+ // Needs to be consistent with the repackaged version of this make target.
+ java_version: "1.8",
srcs: ["src/**/*.java"],
no_framework_libs: true,
@@ -31,7 +33,8 @@
// Build the android.test.runner-minus-junit library
// =================================================
-// This is provided solely for use by the legacy-android-test module.
+// This is only intended for inclusion in the android.test.legacy and
+// legacy-android-test static libraries and must not be used elsewhere.
java_library {
name: "android.test.runner-minus-junit",
@@ -54,4 +57,6 @@
static_libs: ["android.test.runner"],
jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
}
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
index 7145110..021e386 100644
--- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -14,12 +14,16 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.perftests.amteststestapp">
+ package="com.android.frameworks.perftests.amteststestapp">
<application android:name=".TestApplication">
<activity android:name=".TestActivity" android:exported="true"/>
+ <provider
+ android:authorities="com.android.frameworks.perftests.amteststestapp"
+ android:name=".TestContentProvider"
+ android:exported="true" />
<receiver
- android:name=".TestBroadcastReceiver"
- android:exported="true">
+ android:name=".TestBroadcastReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
index 7ea9ba3..1f06121 100644
--- a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
@@ -17,16 +17,19 @@
package com.android.frameworks.perftests.amteststestapp;
import android.app.Activity;
-import android.os.Bundle;
+import android.os.Looper;
+import android.os.MessageQueue;
import com.android.frameworks.perftests.am.util.Constants;
import com.android.frameworks.perftests.am.util.Utils;
public class TestActivity extends Activity {
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Utils.sendTime(getIntent(), Constants.TYPE_ACTIVITY_CREATED);
+ protected void onResume() {
+ super.onResume();
+ Looper.myQueue().addIdleHandler(() -> {
+ Utils.sendTime(getIntent(), Constants.TYPE_TARGET_PACKAGE_START);
+ return false;
+ });
}
}
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java
new file mode 100644
index 0000000..0940578
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java
@@ -0,0 +1,55 @@
+/*
+ * 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.frameworks.perftests.amteststestapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class TestContentProvider extends ContentProvider {
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
new file mode 100644
index 0000000..3bf56ce
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.ContentProviderClient;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ContentProviderPerfTest extends BasePerfTest {
+ /**
+ * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is
+ * running.
+ */
+ @Test
+ public void contentProviderRunning() {
+ runPerfFunction(() -> {
+ startTargetPackage();
+
+ long startTimeNs = System.nanoTime();
+ final ContentProviderClient contentProviderClient =
+ mContext.getContentResolver()
+ .acquireContentProviderClient(TargetPackageUtils.PACKAGE_NAME);
+ final long endTimeNs = System.nanoTime();
+
+ contentProviderClient.close();
+
+ return endTimeNs - startTimeNs;
+ });
+ }
+
+ /**
+ * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is
+ * not running.
+ */
+ @Test
+ public void contentProviderNotRunning() {
+ runPerfFunction(() -> {
+ final long startTimeNs = System.nanoTime();
+ final ContentProviderClient contentProviderClient =
+ mContext.getContentResolver().acquireContentProviderClient(
+ TargetPackageUtils.PACKAGE_NAME);
+ final long endTimeNs = System.nanoTime();
+
+ contentProviderClient.close();
+
+ return endTimeNs - startTimeNs;
+ });
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
index c867141..26a8e7b 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
@@ -62,7 +62,7 @@
sleep();
}
// make sure Application has run
- timeReceiver.getReceivedTimeNs(Constants.TYPE_ACTIVITY_CREATED);
+ timeReceiver.getReceivedTimeNs(Constants.TYPE_TARGET_PACKAGE_START);
Utils.drainBroadcastQueue();
}
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
index 6528028..f35c2fd 100644
--- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
@@ -17,7 +17,7 @@
package com.android.frameworks.perftests.am.util;
public class Constants {
- public static final String TYPE_ACTIVITY_CREATED = "activity_create";
+ public static final String TYPE_TARGET_PACKAGE_START = "target_package_start";
public static final String TYPE_BROADCAST_RECEIVE = "broadcast_receive";
public static final String ACTION_BROADCAST_MANIFEST_RECEIVE =
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 1443fc1..063060f 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -83,7 +83,7 @@
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String WEARABLE_ACTION_GOOGLE =
"com.google.android.wearable.action.GOOGLE";
- private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle
+ private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
diff --git a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
index d46fff4..a7d5ae8 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
@@ -99,6 +99,21 @@
}
@Test
+ public void tonal_rangeTest() {
+ Tonal.ConfigParser config = new Tonal.ConfigParser(InstrumentationRegistry.getContext());
+ for (Tonal.TonalPalette palette : config.getTonalPalettes()) {
+ assertTrue("minHue should be >= to 0.", palette.minHue >= 0);
+ assertTrue("maxHue should be <= to 360.", palette.maxHue <= 360);
+
+ assertTrue("S should be >= to 0.", palette.s[0] >= 0);
+ assertTrue("S should be <= to 1.", palette.s[1] <= 1);
+
+ assertTrue("L should be >= to 0.", palette.l[0] >= 0);
+ assertTrue("L should be <= to 1.", palette.l[1] <= 1);
+ }
+ }
+
+ @Test
public void tonal_blacklistTest() {
// Make sure that palette generation will fail.
final Tonal tonal = new Tonal(InstrumentationRegistry.getContext());
diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk
index 12568a0..291ba78 100644
--- a/tests/JankBench/Android.mk
+++ b/tests/JankBench/Android.mk
@@ -19,7 +19,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-design \
+ $(ANDROID_SUPPORT_DESIGN_TARGETS) \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 60327e5..c8e6c20 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -15,7 +15,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-design \
+ $(ANDROID_SUPPORT_DESIGN_TARGETS) \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
diff --git a/tests/net/OWNERS b/tests/net/OWNERS
index 6f77e04..ce50558 100644
--- a/tests/net/OWNERS
+++ b/tests/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 9aad413..04266c5 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -172,7 +172,7 @@
final int iterations = 1000;
final String expectedAndroidOui = "da:a1:19";
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ MacAddress mac = MacAddress.createRandomUnicastAddressWithGoogleBase();
String stringRepr = mac.toString();
assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -195,6 +195,15 @@
assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
stringRepr.startsWith(expectedLocalOui));
}
+
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.createRandomUnicastAddress();
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
+ }
}
@Test
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6e643a3..e7abede 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -318,6 +318,7 @@
// This test has an inherent race condition in it, and cannot be enabled for continuous testing
// or presubmit tests. It is kept for manual runs and documentation purposes.
+ @Ignore
public void verifyThatNotWaitingForIdleCausesRaceConditions() {
// Bring up a network that we can use to send messages to ConnectivityService.
ConditionVariable cv = waitForConnectivityBroadcasts(1);
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index c9e272c..1afd283 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1446,6 +1446,13 @@
ContainerReaderEntry* entry;
ContainerReader reader(input_stream.get());
+
+ if (reader.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to read file: " << reader.GetError());
+ return false;
+ }
+
while ((entry = reader.Next()) != nullptr) {
if (entry->Type() == ContainerEntryType::kResTable) {
pb::ResourceTable pb_table;
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
index f278aec..d75aea5 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.sdkparcelables
import org.objectweb.asm.ClassVisitor
@@ -25,4 +41,4 @@
super.visit(version, access, name, signature, superName, interfaces)
}
-}
\ No newline at end of file
+}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 3e9d92c..22e8d78 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.sdkparcelables
import org.objectweb.asm.ClassReader
@@ -53,4 +69,4 @@
fun usage() {
System.err.println("Usage: <input jar> <output aidl>")
kotlin.system.exitProcess(1)
-}
\ No newline at end of file
+}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
index 620f798..d6a0a45 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.sdkparcelables
/** A class that uses an ancestor map to find all classes that
diff --git a/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
index edfc825..c9bcbc9 100644
--- a/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
+++ b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.sdkparcelables
import junit.framework.TestCase.assertEquals
diff --git a/wifi/java/android/net/wifi/IRttManager.aidl b/wifi/java/android/net/wifi/IRttManager.aidl
deleted file mode 100644
index 3831809..0000000
--- a/wifi/java/android/net/wifi/IRttManager.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2008 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.net.wifi;
-import android.os.Messenger;
-import android.net.wifi.RttManager;
-
-/**
- * {@hide}
- */
-interface IRttManager
-{
- Messenger getMessenger(in IBinder binder, out int[] key);
- RttManager.RttCapabilities getRttCapabilities();
-}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index dc5ba0c..bdbc149 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -7,24 +7,24 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.List;
+
/** @hide */
@SystemApi
+@Deprecated
@SystemService(Context.WIFI_RTT_SERVICE)
public class RttManager {
@@ -175,12 +175,14 @@
@Deprecated
@SuppressLint("Doclava125")
public Capabilities getCapabilities() {
- return new Capabilities();
+ throw new UnsupportedOperationException(
+ "getCapabilities is not supported in the adaptation layer");
}
/**
* This class describe the RTT capability of the Hardware
*/
+ @Deprecated
public static class RttCapabilities implements Parcelable {
/** @deprecated It is not supported*/
@Deprecated
@@ -314,21 +316,16 @@
};
}
+ /**
+ * This method is deprecated. Please use the {@link WifiRttManager} API.
+ */
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public RttCapabilities getRttCapabilities() {
- synchronized (mCapabilitiesLock) {
- if (mRttCapabilities == null) {
- try {
- mRttCapabilities = mService.getRttCapabilities();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return mRttCapabilities;
- }
+ return mRttCapabilities;
}
/** specifies parameters for RTT request */
+ @Deprecated
public static class RttParams {
/**
* type of destination device being ranged
@@ -511,6 +508,7 @@
}
/** pseudo-private class used to parcel arguments */
+ @Deprecated
public static class ParcelableRttParams implements Parcelable {
@NonNull
@@ -598,12 +596,14 @@
};
}
+ @Deprecated
public static class WifiInformationElement {
/** Information Element ID 0xFF means element is invalid. */
public byte id;
public byte[] data;
}
/** specifies RTT results */
+ @Deprecated
public static class RttResult {
/** mac address of the device being ranged. */
public String bssid;
@@ -755,6 +755,7 @@
/** pseudo-private class used to parcel results. */
+ @Deprecated
public static class ParcelableRttResults implements Parcelable {
public RttResult mResults[];
@@ -847,8 +848,8 @@
}
dest.writeByte(result.LCR.id);
if (result.LCR.id != (byte) 0xFF) {
- dest.writeInt((byte) result.LCR.data.length);
- dest.writeByte(result.LCR.id);
+ dest.writeByte((byte) result.LCR.data.length);
+ dest.writeByteArray(result.LCR.data);
}
dest.writeByte(result.secure ? (byte) 1 : 0);
}
@@ -920,7 +921,7 @@
};
}
-
+ @Deprecated
public static interface RttListener {
public void onSuccess(RttResult[] results);
public void onFailure(int reason, String description);
@@ -928,115 +929,10 @@
}
/**
- * A parcelable that contains rtt client information.
- *
- * @hide
- */
- public static class RttClient implements Parcelable {
- // Package name of RttClient.
- private final String mPackageName;
-
- public RttClient(String packageName) {
- mPackageName = packageName;
- }
-
- protected RttClient(Parcel in) {
- mPackageName = in.readString();
- }
-
- public static final Creator<RttManager.RttClient> CREATOR =
- new Creator<RttManager.RttClient>() {
- @Override
- public RttManager.RttClient createFromParcel(Parcel in) {
- return new RttManager.RttClient(in);
- }
-
- @Override
- public RttManager.RttClient[] newArray(int size) {
- return new RttManager.RttClient[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeString(mPackageName);
- }
-
- public String getPackageName() {
- return mPackageName;
- }
- }
-
- private boolean rttParamSanity(RttParams params, int index) {
- if (mRttCapabilities == null) {
- if(getRttCapabilities() == null) {
- Log.e(TAG, "Can not get RTT capabilities");
- throw new IllegalStateException("RTT chip is not working");
- }
- }
-
- if (params.deviceType != RTT_PEER_TYPE_AP) {
- return false;
- } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
- RTT_TYPE_TWO_SIDED) {
- Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
- return false;
- } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
- !mRttCapabilities.oneSidedRttSupported) {
- Log.e(TAG, "Request " + index + ": One side RTT is not supported");
- return false;
- } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
- !mRttCapabilities.twoSided11McRttSupported) {
- Log.e(TAG, "Request " + index + ": two side RTT is not supported");
- return false;
- } else if(params.bssid == null || params.bssid.isEmpty()) {
- Log.e(TAG,"No BSSID in params");
- return false;
- } else if ( params.numberBurst != 0 ) {
- Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
- return false;
- } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
- Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
- params.numSamplesPerBurst);
- return false;
- } else if (params.numRetriesPerMeasurementFrame < 0 ||
- params.numRetriesPerMeasurementFrame > 3) {
- Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
- params.numRetriesPerMeasurementFrame);
- return false;
- } else if(params.numRetriesPerFTMR < 0 ||
- params.numRetriesPerFTMR > 3) {
- Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
- params.numRetriesPerFTMR);
- return false;
- } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
- Log.e(TAG, "Request " + index + ": LCI is not supported");
- return false;
- } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
- Log.e(TAG, "Request " + index + ": LCR is not supported");
- return false;
- } else if (params.burstTimeout < 1 ||
- (params.burstTimeout > 11 && params.burstTimeout != 15)){
- Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
- return false;
- } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
- return false;
- } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
- return false;
- }
-
- return true;
- }
-
- /**
* Request to start an RTT ranging
+ * <p>
+ * This method is deprecated. Please use the
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)} API.
*
* @param params -- RTT request Parameters
* @param listener -- Call back to inform RTT result
@@ -1045,24 +941,76 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startRanging(RttParams[] params, RttListener listener) {
- int index = 0;
- for(RttParams rttParam : params) {
- if (!rttParamSanity(rttParam, index)) {
- throw new IllegalArgumentException("RTT Request Parameter Illegal");
- }
- index++;
- }
- validateChannel();
- ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
Log.i(TAG, "Send RTT request to RTT Service");
- mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
- 0, putListener(listener), parcelableParams);
+
+ if (!mNewService.isAvailable()) {
+ listener.onFailure(REASON_NOT_AVAILABLE, "");
+ return;
+ }
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ for (RttParams rttParams : params) {
+ if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+ listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+ return;
+ }
+
+ ScanResult reconstructed = new ScanResult();
+ reconstructed.BSSID = rttParams.bssid;
+ if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+ reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ }
+ reconstructed.channelWidth = rttParams.channelWidth;
+ reconstructed.frequency = rttParams.frequency;
+ reconstructed.centerFreq0 = rttParams.centerFreq0;
+ reconstructed.centerFreq1 = rttParams.centerFreq1;
+ builder.addResponder(
+ android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+ }
+ try {
+ mNewService.startRanging(builder.build(), new RangingResultCallback() {
+ @Override
+ public void onRangingFailure(int code) {
+ int localCode = REASON_UNSPECIFIED;
+ if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+ localCode = REASON_NOT_AVAILABLE;
+ }
+ listener.onFailure(localCode, "");
+ }
+
+ @Override
+ public void onRangingResults(List<RangingResult> results) {
+ RttResult[] legacyResults = new RttResult[results.size()];
+ int i = 0;
+ for (RangingResult result : results) {
+ legacyResults[i] = new RttResult();
+ legacyResults[i].status = result.getStatus();
+ legacyResults[i].bssid = result.getMacAddress().toString();
+ legacyResults[i].distance = result.getDistanceMm() / 10;
+ legacyResults[i].distanceStandardDeviation =
+ result.getDistanceStdDevMm() / 10;
+ legacyResults[i].rssi = result.getRssi();
+ legacyResults[i].ts = result.getRangingTimestampUs();
+ }
+ listener.onSuccess(legacyResults);
+ }
+ }, null);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "startRanging: invalid arguments - " + e);
+ listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+ } catch (SecurityException e) {
+ Log.e(TAG, "startRanging: security exception - " + e);
+ listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
+ }
}
+ /**
+ * This method is deprecated and performs no function. Please use the {@link WifiRttManager}
+ * API.
+ */
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void stopRanging(RttListener listener) {
- validateChannel();
- mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+ Log.e(TAG, "stopRanging: unsupported operation - nop");
}
/**
@@ -1074,6 +1022,7 @@
* The client can freely destroy or reuse the callback after {@link RttManager#disableResponder}
* is called.
*/
+ @Deprecated
public abstract static class ResponderCallback {
/** Callback when responder is enabled. */
public abstract void onResponderEnabled(ResponderConfig config);
@@ -1089,18 +1038,18 @@
* Note calling this method with the same callback when the responder is already enabled won't
* change the responder state, a cached {@link ResponderConfig} from the last enabling will be
* returned through the callback.
+ * <p>
+ * This method is deprecated and will throw an {@link UnsupportedOperationException}
+ * exception. Please use the {@link WifiRttManager} API to perform a Wi-Fi Aware peer-to-peer
+ * ranging.
*
* @param callback Callback for responder enabling/disabling result.
* @throws IllegalArgumentException If {@code callback} is null.
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void enableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = putListenerIfAbsent(callback);
- mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "enableResponder is not supported in the adaptation layer");
}
/**
@@ -1109,22 +1058,18 @@
* <p>
* Calling this method when responder isn't enabled won't have any effect. The callback can be
* reused for enabling responder after this method is called.
+ * <p>
+ * This method is deprecated and will throw an {@link UnsupportedOperationException}
+ * exception. Please use the {@link WifiRttManager} API to perform a Wi-Fi Aware peer-to-peer
+ * ranging.
*
* @param callback The same callback used for enabling responder.
* @throws IllegalArgumentException If {@code callback} is null.
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void disableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = removeListener(callback);
- if (key == INVALID_KEY) {
- Log.e(TAG, "responder not enabled yet");
- return;
- }
- mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "disableResponder is not supported in the adaptation layer");
}
/**
@@ -1133,6 +1078,7 @@
*
* @see ScanResult
*/
+ @Deprecated
public static class ResponderConfig implements Parcelable {
// TODO: make all fields final once we can get mac address from responder HAL APIs.
@@ -1238,195 +1184,33 @@
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private static final int INVALID_KEY = 0;
-
- private final Context mContext;
- private final IRttManager mService;
- private final SparseArray mListenerMap = new SparseArray();
- private final Object mListenerMapLock = new Object();
- private final Object mCapabilitiesLock = new Object();
-
+ private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
- private int mListenerKey = 1;
- private AsyncChannel mAsyncChannel;
/**
* Create a new WifiScanner instance.
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
- * @param context the application context
- * @param service the Binder interface
- * @param looper Looper for running the callbacks.
+ * @param service the new WifiRttManager service
*
* @hide
*/
- public RttManager(Context context, IRttManager service, Looper looper) {
- mContext = context;
- mService = service;
- Messenger messenger = null;
- int[] key = new int[1];
- try {
- Log.d(TAG, "Get the messenger from " + mService);
- messenger = mService.getMessenger(new Binder(), key);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ public RttManager(Context context, WifiRttManager service) {
+ mNewService = service;
- if (messenger == null) {
- throw new IllegalStateException("getMessenger() returned null! This is invalid.");
- }
+ boolean rttSupported = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT);
- mAsyncChannel = new AsyncChannel();
-
- Handler handler = new ServiceHandler(looper);
- mAsyncChannel.connectSync(mContext, handler, messenger);
- // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
- // synchronously, which causes RttService to receive the wrong replyTo value.
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
- new RttClient(context.getPackageName()));
- mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+ mRttCapabilities = new RttCapabilities();
+ mRttCapabilities.oneSidedRttSupported = rttSupported;
+ mRttCapabilities.twoSided11McRttSupported = rttSupported;
+ mRttCapabilities.lciSupported = false;
+ mRttCapabilities.lcrSupported = false;
+ mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+ mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+ mRttCapabilities.responderSupported = false;
+ mRttCapabilities.secureRttSupported = false;
}
-
- private void validateChannel() {
- if (mAsyncChannel == null) throw new IllegalStateException(
- "No permission to access and change wifi or a bad initialization");
- }
-
- private int putListener(Object listener) {
- if (listener == null) return INVALID_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- }
- return key;
- }
-
- // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
- private int putListenerIfAbsent(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int key = getListenerKey(listener);
- if (key != INVALID_KEY) {
- return key;
- }
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- return key;
- }
-
- }
-
- private Object getListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- return listener;
- }
- }
-
- private int getListenerKey(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int index = mListenerMap.indexOfValue(listener);
- if (index == -1) {
- return INVALID_KEY;
- } else {
- return mListenerMap.keyAt(index);
- }
- }
- }
-
- private Object removeListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- mListenerMap.remove(key);
- return listener;
- }
- }
-
- private int removeListener(Object listener) {
- int key = getListenerKey(listener);
- if (key == INVALID_KEY) return key;
- synchronized (mListenerMapLock) {
- mListenerMap.remove(key);
- return key;
- }
- }
-
- private class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "RTT manager get message: " + msg.what);
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- getLooper().quit();
- return;
- }
-
- Object listener = getListener(msg.arg2);
- if (listener == null) {
- Log.e(TAG, "invalid listener key = " + msg.arg2 );
- return;
- } else {
- Log.i(TAG, "listener key = " + msg.arg2);
- }
-
- switch (msg.what) {
- /* ActionListeners grouped together */
- case CMD_OP_SUCCEEDED :
- reportSuccess(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_FAILED :
- reportFailure(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_ABORTED :
- ((RttListener) listener).onAborted();
- removeListener(msg.arg2);
- break;
- case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
- ResponderConfig config = (ResponderConfig) msg.obj;
- ((ResponderCallback) (listener)).onResponderEnabled(config);
- break;
- case CMD_OP_ENALBE_RESPONDER_FAILED:
- ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
- removeListener(msg.arg2);
- break;
- default:
- if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
- return;
- }
- }
-
- void reportSuccess(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
- ((RttListener) listener).onSuccess(parcelableResults.mResults);
- }
-
- void reportFailure(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- Bundle bundle = (Bundle) msg.obj;
- ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
- }
- }
-
}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index c46789c..8024bf0 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -273,27 +273,6 @@
public RadioChainInfo[] radioChainInfos;
/**
- * @hide
- * Update RSSI of the scan result
- * @param previousRssi
- * @param previousSeen
- * @param maxAge
- */
- public void averageRssi(int previousRssi, long previousSeen, int maxAge) {
-
- if (seen == 0) {
- seen = System.currentTimeMillis();
- }
- long age = seen - previousSeen;
-
- if (previousSeen > 0 && age > 0 && age < maxAge/2) {
- // Average the RSSI with previously seen instances of this scan result
- double alpha = 0.5 - (double) age / (double) maxAge;
- level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha);
- }
- }
-
- /**
* Status indicating the scan result does not correspond to a user's saved configuration
* @hide
* @removed
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 8d1a00b..ddcf327 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.pm.PackageManager;
import android.net.IpConfiguration;
@@ -439,6 +440,7 @@
/**
* @hide
*/
+ @NonNull
private IpConfiguration mIpConfiguration;
/**
@@ -915,6 +917,9 @@
* Does not guarantee that the returned address is valid for use.
*/
public MacAddress getRandomizedMacAddress() {
+ if (mRandomizedMacAddress == null) {
+ mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
+ }
return mRandomizedMacAddress;
}
@@ -1615,6 +1620,7 @@
creatorUid = -1;
shared = true;
dtimInterval = 0;
+ mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
}
/**
@@ -2023,6 +2029,7 @@
/** @hide */
public void setIpConfiguration(IpConfiguration ipConfiguration) {
+ if (ipConfiguration == null) ipConfiguration = new IpConfiguration();
mIpConfiguration = ipConfiguration;
}
@@ -2306,7 +2313,7 @@
config.allowedGroupCiphers = readBitSet(in);
config.enterpriseConfig = in.readParcelable(null);
- config.mIpConfiguration = in.readParcelable(null);
+ config.setIpConfiguration(in.readParcelable(null));
config.dhcpServer = in.readString();
config.defaultGwMacAddress = in.readString();
config.selfAdded = in.readInt() != 0;
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index e7377c1..8a3a7f5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -176,6 +176,8 @@
@Test
public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
WifiConfiguration config = new WifiConfiguration();
+ assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
@@ -185,6 +187,8 @@
@Test
public void testSetRandomizedMacAddress_ChangesSavedAddress() {
WifiConfiguration config = new WifiConfiguration();
+ assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
config.setRandomizedMacAddress(macToChangeInto);
MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 71ca068..f3a78bd 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -611,6 +611,7 @@
/**
* Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl.
*/
+ @Test
public void testWatchLocalOnlyHotspot() throws Exception {
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
index 1e8382f..e8e4dc2 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -59,10 +59,10 @@
*/
@Test
public void testChannelFinalize() throws Exception {
- WifiP2pManager.Channel channel = new WifiP2pManager.Channel(mContextMock,
- mTestLooper.getLooper(), null, null, mDut);
-
- leakageDetectorRule.assertUnreleasedResourceCount(channel, 1);
+ try (WifiP2pManager.Channel channel = new WifiP2pManager.Channel(mContextMock,
+ mTestLooper.getLooper(), null, null, mDut)) {
+ leakageDetectorRule.assertUnreleasedResourceCount(channel, 1);
+ }
}
/**